<details markdown="1">
<summary>目录</summary>
</details>
对于通过导出组件 intent 传递的序列化对象,必须进行 try...catch 处理,以避免数据非法导致应用崩溃。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
try {
Intent mIntent = getIntent();
//String msg = intent.getStringExtra("data");
Person mPerson = (Person)mIntent.getSerializableExtra(ObjectDemo.SER_KEY)
//textView.setText(msg);
} catch (ClassNotFoundException exp) {
// ......
}
}
}
对于通过 intent getAction 方法获取数据时,必须进行 try...catch 处理,以避免空指针异常导致应用崩溃。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
try {
Intent mIntent = getIntent();
if mIntent.getAction().equals("StartNewWorld") {
// ......
}
// ......
} catch (NullPointerException exp) {
// ......
}
}
}
对于通过 intent getSerializableExtra 方法获取数据时,必须进行 try...catch 处理,以避免类型转换异常导致应用崩溃。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
try {
Intent mIntent = getIntent();
Person mPerson = (Person)mIntent.getSerializableExtra(ObjectDemo.SER_KEY)
// ......
} catch (ClassCastException exp) {
// ......
}
}
}
同 1.1.3
release 版本禁止在 logcat 输出信息。
public class MainActivity extends Activity {
String DEBUG = "debug_version";
protected void onCreate(Bundle savedInstanceState) {
// ......
if (DEBUG == "debug_version") {
Log.d("writelog", "start activity");
}
// ......
}
}
对于设置 minsdk <= 18 的应用,禁止调用 addJavaScriptInterface 方法。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
mWebView = new WebView(this);
if (Build.VERSION.SDK_INT > 18) {
mWebView.addJavascriptInterface(new wPayActivity.InJavaScriptLocalObj(this), "local_obj");
}
// ......
}
}
如非必要,setJavaScriptEnabled 应设置为 false 。加载本地 html ,应校验 html 页面完整性,以避免 xss 攻击。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
mWebView = new WebView(this);
mWebView.getSettings().setJavaScriptEnabled(false);
// ......
}
}
建议禁止使用 File 域协议,以避免过滤不当导致敏感信息泄露。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
mWebView = new WebView(this);
mWebView.getSettings().setAllowFileAccess(false);
// ......
}
}
建议 setSavePassword 的设置为 false ,避免明文保存网站密码。
建议禁止使用 File 域协议,以避免过滤不当导致敏感信息泄露。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
mWebView = new WebView(this);
mWebView.getSettings().setSavePassword(false);
// ......
}
}
webview 组件加载网页发生证书认证错误时,不能直接调用 handler.proceed() 忽略错误,应当处理当前场景是否符合业务预期,以避免中间人攻击劫持。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
mWebView = new WebView(this);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// must check error
check_error();
handler.proceed();
}
}
// ......
}
}
自定义 HostnameVerifier 类后,必须实现 verify 方法校验域名,以避免中间人攻击劫持。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
HostnameVerifier hnv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// must to do
isValid = checkHostName(hostname);
return isValid;
}
};
// ......
}
}
自定义 X509TrustManager 类后,必须实现 checkServerTrusted 方法校验服务器证书,以避免中间人攻击劫持。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
TrustManager tm = new X509TrustManager() {
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// must to do
check_server_valid();
}
};
// ......
}
}
禁止调用 setHostnameVerifier 方法设置 ALLOW_ALL_HOSTNAME_VERIFIER 属性,以避免中间人攻击劫持。
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ......
SchemeRegistry schemeregistry = new SchemeRegistry();
SSLSocketFactory sslsocketfactory = SSLSocketFactory.getSocketFactory();
// set STRICT_HOSTNAME_VERIFIER
sslsocketfactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
// ......
}
}
禁止设置 PermissionGroup 属性为空。
对于自定义权限的 protectionLevel 属性设置,建议设置为 signature 或 signatureOrSystem。
最小范围和最小权限使用 sharedUserId 设置。
如非产品功能需要,建议设置 allowBackup 为 false。
<application android:allowBackup="false">
</application>
release 版本禁止设置 debuggable 为 true。
<application android:debuggable="false">
</application>
Web后台系统应默认使用预编译绑定变量的形式创建sql语句,保持查询语句和数据相分离。以从本质上避免SQL注入风险。
如使用Mybatis作为持久层框架,应通过#{}语法进行参数绑定,MyBatis 会创建 PreparedStatement
参数占位符,并通过占位符安全地设置参数。
示例:JDBC
String custname = request.getParameter("name");
String query = "SELECT * FROM user_data WHERE user_name = ? ";
PreparedStatement pstmt = connection.prepareStatement( query );
pstmt.setString( 1, custname);
ResultSet results = pstmt.executeQuery( );
Mybatis
<select id="queryRuleIdByApplicationId" parameterType="java.lang.String" resultType="java.lang.String">
select rule_id from scan_rule_sqlmap_tab where application_id=#{applicationId}
</select>
应避免外部输入未经过滤直接拼接到SQL语句中,或者通过Mybatis中的${}传入SQL语句(即使使用PreparedStatement,SQL语句直接拼接外部输入也同样有风险。例如Mybatis中部分参数通过${}传入SQL语句后实际执行时调用的是PreparedStatement.execute(),同样存在注入风险)。
对于表名、列名等无法进行预编译的场景,比如外部数据拼接到order by, group by语句中,需通过白名单的形式对数据进行校验,例如判断传入列名是否存在、升降序仅允许输入“ASC”和“DESC”、表明列名仅允许输入字符、数字、下划线等。参考示例:
public String someMethod(boolean sortOrder) {
String SQLquery = "some SQL ... order by Salary " + (sortOrder ? "ASC" : "DESC");`
...
须在服务器端采用白名单方式对上传或下载的文件类型、大小进行严格的限制。仅允许业务所需文件类型上传,避免上传.jsp、.jspx、.class、.java等可执行文件。参考示例:
String file_name = file.getOriginalFilename();
String[] parts = file_name.split("\\.");
String suffix = parts[parts.length - 1];
switch (suffix){
case "jpeg":
suffix = ".jpeg";
break;
case "jpg":
suffix = ".jpg";
break;
case "bmp":
suffix = ".bmp";
break;
case "png":
suffix = ".png";
break;
default:
//handle error
return "error";
}
禁止外部文件存储于WEB容器的可执行目录(appBase)。建议保存在专门的文件服务器中。
文件目录避免外部参数拼接。保存文件目录建议后台写死并对文件名进行校验(字符类型、长度)。建议文件保存时,将文件名替换为随机字符串。
如因业务需要不能满足1.2.3的要求,文件路径、文件命中拼接了不可行数据,需判断请求文件名和文件路径参数中是否存在../或..\(仅windows), 如存在应判定路径非法并拒绝请求。
服务器访问不可信地址时,禁止访问私有地址段及内网域名。
// 以RFC定义的专有网络为例,如有自定义私有网段亦应加入禁止访问列表。
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8
建议通过URL解析函数进行解析,获取host或者domain后通过DNS获取其IP,然后和内网地址进行比较。
对已校验通过地址进行访问时,应关闭跟进跳转功能。
参考示例:
httpConnection = (HttpURLConnection) Url.openConnection();
httpConnection.setFollowRedirects(false);
读取外部传入XML文件时,XML解析器初始化过程中设置关闭DTD解析。
参考示例:
javax.xml.parsers.DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
……
}
org.dom4j.io.SAXReader
saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
saxReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
saxReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
org.jdom2.input.SAXBuilder
SAXBuilder builder = new SAXBuilder();
builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
Document doc = builder.build(new File(fileName));
org.xml.sax.XMLReader
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
响应包的HTTP头“Content-Type”必须正确配置响应包的类型,禁止非HTML类型的响应包设置为“text/html”。此举会使浏览器在直接访问链接时,将非HTML格式的返回报文当做HTML解析,增加反射型XSS的触发几率。
建议添加“X-Content-Type-Options”响应头并将其值设置为“nosniff”,可避免部分浏览器根据其“Content-Sniff”特性,将一些非“text/html”类型的响应作为HTML解析,增加反射型XSS的触发几率。
控制用户登录鉴权的Cookie字段 应当设置HttpOnly属性以防止被XSS漏洞/JavaScript操纵泄漏。
设置X-Frame-Options响应头,并根据需求合理设置其允许范围。该头用于指示浏览器禁止当前页面在frame、iframe、embed等标签中展现。从而避免点击劫持问题。它有三个可选的值:
DENY: 浏览器会拒绝当前页面加载任何frame页面;
SAMEORIGIN:则frame页面的地址只能为同源域名下的页面
ALLOW-FROM origin:可以定义允许frame加载的页面地址。
Access-Control-Allow-Origin
当需要配置CORS跨域时,应对请求头的Origin值做严格过滤。
...
String currentOrigin = request.getHeader("Origin");
if (currentOrigin.equals("https://domain.qq.com")) {
response.setHeader("Access-Control-Allow-Origin", currentOrigin);
}