软件安全不是安全软件。此处我们关注的主题包括身份验证、Access Control、机密性、加密和权限管理。
server.servlet.session.cookie.persistent=true
session_set_cookie_params(time()+60*60*24*365*10, "/", "www.example.com", false, true);
Secure
标记设置为 true
。Secure
标记。如果设置了该标记,那么浏览器只会通过 HTTPS 发送 Cookie。通过未加密的通道发送 Cookie 将使其受到 Network Sniffing 攻击,因此该 secure 标记有助于保护 Cookie 值的机密性。如果 Cookie 包含私人数据或带有会话标识符,那么该标记尤其重要。Secure
标记的情况下将会话 Cookie 添加到响应中。
...
<configuration>
<system.web>
<authentication mode="Forms">
<forms requireSSL="false" loginUrl="login.aspx">
</forms>
</authentication>
</system.web>
</configuration>
...
Secure
标记,那么在 HTTPS 请求过程中发送的 Cookie 也会在随后的 HTTP 请求过程中被发送。通过未加密的无线连接监听网络通信对攻击者而言十分简单,因此通过 HTTP 发送 Cookie(特别是具有会话 ID 的 Cookie)可能会危及应用程序安全。Secure
标记。如果设置了该标记,那么浏览器只会通过 HTTPS 发送 Cookie。通过未加密的通道发送 Cookie 将使其受到 Network Sniffing 攻击,因此该 secure 标记有助于保护 Cookie 值的机密性。如果 Cookie 包含私人数据或带有会话标识符,那么该标记尤其重要。Secure
标记。
server.servlet.session.cookie.secure=false
Secure
标记,那么在 HTTPS 请求过程中发送的 Cookie 也会在随后的 HTTP 请求过程中被发送。攻击者随后可截取未加密的网络信息流(通过无线网络时十分容易),从而危及 cookie 安全。Secure
标记设置为 true
。Secure
标记。如果设置了该标记,那么浏览器只会通过 HTTPS 发送 cookie。通过未加密的通道发送 cookie 将使其受到网络截取攻击,因此安全标记有助于保护 cookie 值的保密性。如果 cookie 包含私人数据或带有会话标识符,那么该标记尤其重要。Secure
标记的情况下将 cookie 添加到响应中。
...
setcookie("emailCookie", $email, 0, "/", "www.example.com");
...
Secure
标记,那么在 HTTPS 请求过程中发送的 Cookie 也会在随后的 HTTP 请求过程中被发送。攻击者随后可截取未加密的网络信息流(通过无线网络时十分容易),从而危及 cookie 安全。SESSION_COOKIE_SECURE
属性设置为 True
或将其设置为 False
。Secure
标记。如果设置了该标记,那么浏览器只会通过 HTTPS 发送 cookie。通过未加密的通道发送 cookie 将使其受到网络截取攻击,因此安全标记有助于保护 cookie 值的保密性。如果 cookie 包含私人数据、会话标识符,或带有 CSRF 标记,那么该标记尤其重要。Secure
位。
...
MIDDLEWARE = (
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'csp.middleware.CSPMiddleware',
'django.middleware.security.SecurityMiddleware',
...
)
...
Secure
标记,那么在 HTTPS 请求过程中发送的 Cookie 也会在随后的 HTTP 请求过程中被发送。攻击者随后可截取未加密的网络信息流(通过无线网络时十分容易),从而危及 cookie 安全。
<cfquery name = "GetCredentials" dataSource = "master">
SELECT Username, Password
FROM Credentials
WHERE DataSource="users"
</cfquery>
...
<cfquery name = "GetSSNs" dataSource = "users"
username = "#Username#" password = "#Password#">
SELECT SSN
FROM Users
</cfquery>
...
master
表具有访问权限的人都能读取 Username
和 Password
的值。任何心怀不轨的雇员可以利用手中掌握的信息访问权限入侵系统。
...
<cfquery name = "GetSSNs" dataSource = "users"
username = "scott" password = "tiger">
SELECT SSN
FROM Users
</cfquery>
...
...
Credentials.basic("hardcoded-username", password);
...
...
PARAMETERS: p_input TYPE sy-mandt.
SELECT *
FROM employee_records
CLIENT SPECIFIED
INTO TABLE tab_output
WHERE mandt = p_input.
...
SY-MANDT
以外的客户端查看员工详细信息。SECRET_KEY
设置工具进行存储。如果 SECRET_KEY
泄露,那么攻击者不仅能伪造会话数据,如果应用程序使用 Pickle 将会话数据序列化成 cookie,攻击者还能够生成恶意 Pickle 数据,这些数据一旦反序列化即可执行任意代码。Host
标头,攻击者就会有机会发送假的 Host
值,该值可用于跨站请求伪造、缓存中毒攻击和电子邮件中的中毒链接。*
”指定为 ALLOWED_HOSTS
设置中的条目。此设置被 django.http.HttpRequest.get_host()
用来验证 Host
标头。值“*
”将允许在 Host
标头中使用任何主机。攻击者可将其用于缓存中毒攻击或电子邮件中的中毒链接。Host
值来构建,用来引用提供密码重置功能的网站,从而避免使用硬编码的 URL。例如:
...
def reset_password(request):
url = "http://%s/new_password/?token=%s" % (request.get_host(), generate_token())
send_email(reset_link=url)
redirect("home")
...
Host
标头值来尝试重置受害者的密码。受害者将收到内含密码重置系统链接的电子邮件,如果该受害者决定访问该链接,则会访问攻击者控制的网站,而该网站将提供假表单来收集受害者的凭证。SECRET_KEY
,则攻击者将能够在会话 cookie 中存储任意数据,在服务器中对这些数据进行反序列化,就可以执行任意代码。SECRET_KEY
在 settings.py
配置文件中进行了硬编码,则以下查看方法可使攻击者有机会窃取它。
...
def some_view_method(request):
url = request.GET['url']
if "http://" in url:
content = urllib.urlopen(url)
return HttpResponse(content)
...
Example 1
方法会通过检查 URL 中是否存在“http://”来检查 url
参数是否为有效 URL。恶意攻击者可能会发送以下 URL 来泄露可能包含 SECRET_KEY
的 settings.py
配置文件:
file://proc/self/cwd/app/settings.py#http://
script
标签:
<script src="http://www.example.com/js/fancyWidget.js"></script>
www.example.com
内容来加载自己的 JavaScript。
permissions := strconv.Atoi(os.Getenv("filePermissions"));
fMode := os.FileMode(permissions)
os.chmod(filePath, fMode);
...
String permissionMask = System.getProperty("defaultFileMask");
Path filePath = userFile.toPath();
...
Set<PosixFilePermission> perms = PosixFilePermissions.fromString(permissionMask);
Files.setPosixFilePermissions(filePath, perms);
...
$rName = $_GET['publicReport'];
chmod("/home/". authenticateUser . "/public_html/" . rName,"0755");
...
publicReport
提供恶意值(例如,../../localuser/public_html/.htpasswd
),那么应用程序将允许攻击者读取指定文件。
...
$mask = $CONFIG_TXT['perms'];
chmod($filename,$mask);
...
permissions = os.getenv("filePermissions");
os.chmod(filePath, permissions);
...
...
rName = req['publicReport']
File.chmod("/home/#{authenticatedUser}/public_html/#{rName}", "0755")
...
publicReport
提供恶意值(例如,../../localuser/public_html/.htpasswd
),那么应用程序将允许攻击者读取指定文件。
...
mask = config_params['perms']
File.chmod(filename, mask)
...
SECURE_CROSS_ORIGIN_OPENER_POLICY = 'unsafe-none'
<authorization>
<allow verbs="GET,POST" users="admin"/>
<deny verbs="GET,POST"users="*" />
</authorization>
<security-constraint>
<display-name>Admin Constraint</display-name>
<web-resource-collection>
<web-resource-name>Admin Area</web-resource-name>
<url-pattern>/pages/index.jsp</url-pattern>
<url-pattern>/admin/*.do</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<description>only admin</description>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<http-method>
标签中未明确定义 HEAD 这一类谓词,或许可以通过将 GET 或 POST 请求替换为 HEAD 请求来执行管理功能。为使 HEAD 请求执行管理功能,必须满足条件 3 - 应用程序必须根据 POST 以外的谓词来执行命令。部分 Web/应用程序服务器将接受任意非标准 HTTP 谓词并像收到 GET 请求一样做出响应。如果出现这种情况,攻击者将可以在请求中使用任意谓词查看管理页面。
GET /admin/viewUsers.do HTTP/1.1
Host: www.example.com
FOO /admin/viewUsers.do HTTP/1.1
Host: www.example.com
FORM GenerateReceiptURL CHANGING baseUrl TYPE string.
DATA: r TYPE REF TO cl_abap_random,
var1 TYPE i,
var2 TYPE i,
var3 TYPE n.
GET TIME.
var1 = sy-uzeit.
r = cl_abap_random=>create( seed = var1 ).
r->int31( RECEIVING value = var2 ).
var3 = var2.
CONCATENATE baseUrl var3 ".html" INTO baseUrl.
ENDFORM.
CL_ABAP_RANDOM->INT31
函数为它生成的收据页面生成“唯一”的标识符。由于 CL_ABAP_RANDOM
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
string GenerateReceiptURL(string baseUrl) {
Random Gen = new Random();
return (baseUrl + Gen.Next().toString() + ".html");
}
Random.Next()
函数为它生成的收据页面生成“唯一”的标识符。由于 Random.Next()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
char* CreateReceiptURL() {
int num;
time_t t1;
char *URL = (char*) malloc(MAX_URL);
if (URL) {
(void) time(&t1);
srand48((long) t1); /* use time to set seed */
sprintf(URL, "%s%d%s", "http://test.com/", lrand48(), ".html");
}
return URL;
}
lrand48()
函数为它生成的收据页面生成“唯一”的标识符。由于 lrand48()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器,就会更安全些。
<cfoutput>
Receipt: #baseUrl##Rand()#.cfm
</cfoutput>
Rand()
函数为它生成的收据页面生成“唯一”的标识符。由于 Rand()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
import "math/rand"
...
var mathRand = rand.New(rand.NewSource(1))
rsa.GenerateKey(mathRand, 2048)
rand.New()
函数生成 RSA 密钥的随机性。由于 rand.New()
是统计学的 PRNG,攻击者很容易猜到其生成的值。
String GenerateReceiptURL(String baseUrl) {
Random ranGen = new Random();
ranGen.setSeed((new Date()).getTime());
return (baseUrl + ranGen.nextInt(400000000) + ".html");
}
Random.nextInt()
函数为它生成的收据页面生成“唯一”的标识符。由于 Random.nextInt()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
function genReceiptURL (baseURL){
var randNum = Math.random();
var receiptURL = baseURL + randNum + ".html";
return receiptURL;
}
Math.random()
函数为它生成的收据页面生成“唯一”的标识符。由于 Math.random()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
fun GenerateReceiptURL(baseUrl: String): String {
val ranGen = Random(Date().getTime())
return baseUrl + ranGen.nextInt(400000000).toString() + ".html"
}
Random.nextInt()
函数为它所生成的收据页面生成独特的标识符。由于 Random.nextInt()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
function genReceiptURL($baseURL) {
$randNum = rand();
$receiptURL = $baseURL . $randNum . ".html";
return $receiptURL;
}
rand()
函数为它生成的收据页面生成“唯一”的标识符。由于 rand()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
CREATE or REPLACE FUNCTION CREATE_RECEIPT_URL
RETURN VARCHAR2
AS
rnum VARCHAR2(48);
time TIMESTAMP;
url VARCHAR2(MAX_URL)
BEGIN
time := SYSTIMESTAMP;
DBMS_RANDOM.SEED(time);
rnum := DBMS_RANDOM.STRING('x', 48);
url := 'http://test.com/' || rnum || '.html';
RETURN url;
END
DBMS_RANDOM.SEED()
函数为它生成的收据页面生成“唯一”的标识符。由于 DBMS_RANDOM.SEED()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器,就会更安全些。
def genReceiptURL(self,baseURL):
randNum = random.random()
receiptURL = baseURL + randNum + ".html"
return receiptURL
rand()
函数为它生成的收据页面生成“唯一”的标识符。由于 rand()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
def generateReceiptURL(baseUrl) {
randNum = rand(400000000)
return ("#{baseUrl}#{randNum}.html");
}
Kernel.rand()
函数为它生成的收据页面生成“唯一”的标识符。由于 Kernel.rand()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。
def GenerateReceiptURL(baseUrl : String) : String {
val ranGen = new scala.util.Random()
ranGen.setSeed((new Date()).getTime())
return (baseUrl + ranGen.nextInt(400000000) + ".html")
}
Random.nextInt()
函数为它生成的收据页面生成“唯一”的标识符。由于 Random.nextInt()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。
sqlite3_randomness(10, &reset_token)
...
Function genReceiptURL(baseURL)
dim randNum
randNum = Rnd()
genReceiptURL = baseURL & randNum & ".html"
End Function
...
Rnd()
函数为它生成的收据页面生成“唯一”的标识符。由于 Rnd()
是统计学的 PRNG,攻击者很容易猜到其生成的字符串。尽管收据系统的底层设计并不完善,但若使用不会生成可预测收据标识符的随机数生成器(如密码学的 PRNG),就会更安全些。CL_ABAP_RANDOM
类或其变体)使用特定常数值作为种子,则通过 GET_NEXT
、INT
和通过可返回或分配值的类似方法返回的值对可以收集大量 PRNG 输出的攻击者来说是可预测的。random_gen1
来预测对象 random_gen2
生成的值。
DATA: random_gen1 TYPE REF TO cl_abap_random,
random_gen2 TYPE REF TO cl_abap_random,
var1 TYPE i,
var2 TYPE i.
random_gen1 = cl_abap_random=>create( seed = '1234' ).
DO 10 TIMES.
CALL METHOD random_gen1->int
RECEIVING
value = var1.
WRITE:/ var1.
ENDDO.
random_gen2 = cl_abap_random=>create( seed = '1234' ).
DO 10 TIMES.
CALL METHOD random_gen2->int
RECEIVING
value = var2.
WRITE:/ var2.
ENDDO.
random_gen1
和 random_gen2
设置了相同的种子,因此 var1 = var2
rand()
)使用特定值作为种子(使用类似 srand(unsigned int)
的函数),则通过 rand()
和通过可返回或分配值的类似方法返回的值对可以收集一定数量 PRNG 输出的攻击者来说是可预测的。
srand(2223333);
float randomNum = (rand() % 100);
syslog(LOG_INFO, "Random: %1.2f", randomNum);
randomNum = (rand() % 100);
syslog(LOG_INFO, "Random: %1.2f", randomNum);
srand(2223333);
float randomNum2 = (rand() % 100);
syslog(LOG_INFO, "Random: %1.2f", randomNum2);
randomNum2 = (rand() % 100);
syslog(LOG_INFO, "Random: %1.2f", randomNum2);
srand(1231234);
float randomNum3 = (rand() % 100);
syslog(LOG_INFO, "Random: %1.2f", randomNum3);
randomNum3 = (rand() % 100);
syslog(LOG_INFO, "Random: %1.2f", randomNum3);
randomNum1
和 randomNum2
的结果设置相同的种子,因此在为伪随机数值生成器 srand(2223333)
设置种子的调用后,对 rand()
的每次调用都将会以相同的调用顺序产生相同的输出。例如,输出可能与以下内容相似:
Random: 32.00
Random: 73.00
Random: 32.00
Random: 73.00
Random: 15.00
Random: 75.00
math.Rand.New(Source)
的函数),则通过 math.Rand.Int()
和通过可返回或分配值的类似方法返回的值对可以收集大量 PRNG 输出的攻击者来说是可预测的。
randomGen := rand.New(rand.NewSource(12345))
randomInt1 := randomGen.nextInt()
randomGen.Seed(12345)
randomInt2 := randomGen.nextInt()
randomGen.Seed(12345)
) 设置种子的调用之后,每次调用 nextInt()
都会产生相同的输出和顺序。Random
)使用特定值作为种子(使用诸如 Random.setSeed()
的函数),则通过 Random.nextInt()
和通过可返回或分配值的类似方法返回的值对可以收集大量 PRNG 输出的攻击者来说是可预测的。Random
对象 randomGen1
来预测 Random
对象 randomGen2
生成的值。
Random randomGen1 = new Random();
randomGen1.setSeed(12345);
int randomInt1 = randomGen1.nextInt();
byte[] bytes1 = new byte[4];
randomGen1.nextBytes(bytes1);
Random randomGen2 = new Random();
randomGen2.setSeed(12345);
int randomInt2 = randomGen2.nextInt();
byte[] bytes2 = new byte[4];
randomGen2.nextBytes(bytes2);
randomGen1
和 randomGen2
设置了相同的种子,因此 randomInt1 == randomInt2
,且数组 bytes1[]
和 bytes2[]
的相应值是相等的。Random
)使用特定值作为种子(使用诸如 Random(Int)
的函数),则通过 Random.nextInt()
和通过可返回或分配值的类似方法返回的值对可以收集大量 PRNG 输出的攻击者来说是可预测的。Random
对象 randomGen1
来预测 Random
对象 randomGen2
生成的值。
val randomGen1 = Random(12345)
val randomInt1 = randomGen1.nextInt()
val byteArray1 = ByteArray(4)
randomGen1.nextBytes(byteArray1)
val randomGen2 = Random(12345)
val randomInt2 = randomGen2.nextInt()
val byteArray2 = ByteArray(4)
randomGen2.nextBytes(byteArray2)
randomGen1
和 randomGen2
设置了相同的种子,因此 randomInt1 == randomInt2
,且数组 byteArray1
和 byteArray2
的相应值是相等的。
...
import random
random.seed(123456)
print "Random: %d" % random.randint(1,100)
print "Random: %d" % random.randint(1,100)
print "Random: %d" % random.randint(1,100)
random.seed(123456)
print "Random: %d" % random.randint(1,100)
print "Random: %d" % random.randint(1,100)
print "Random: %d" % random.randint(1,100)
...
random.seed(123456)
) 设置种子的调用后,对 randint()
的每次调用都将会导致按相同的顺序显示相同的输出。例如,输出可能与以下内容相似:
Random: 81
Random: 80
Random: 3
Random: 81
Random: 80
Random: 3
Random
)使用特定值作为种子(使用诸如 Random.setSeed()
的函数),则通过 Random.nextInt()
和通过可返回或分配值的类似方法返回的值对可以收集大量 PRNG 输出的攻击者来说是可预测的。Random
对象 randomGen1
来预测 Random
对象 randomGen2
生成的值。
val randomGen1 = new Random()
randomGen1.setSeed(12345)
val randomInt1 = randomGen1.nextInt()
val bytes1 = new byte[4]
randomGen1.nextBytes(bytes1)
val randomGen2 = new Random()
randomGen2.setSeed(12345)
val randomInt2 = randomGen2.nextInt()
val bytes2 = new byte[4]
randomGen2.nextBytes(bytes2)
randomGen1
和 randomGen2
设置了相同的种子,因此 randomInt1 == randomInt2
,且数组 bytes1[]
和 bytes2[]
的相应值是相等的。CL_ABAP_RANDOM
(或其变体)不应使用受污染参数进行初始化。这样做可使攻击者控制作为伪随机数值生成器种子的值,因此能够预测由调用方法产生的值的顺序,这些方法包括但不限于: GET_NEXT
, INT
, FLOAT
, PACKED
.rand()
)的随机或伪随机值(如 srand()
)的函数,不应该使用受污染的参数进行调用。这样做可使攻击者控制作为伪随机数值生成器种子的值,因此能够预测由调用随机数值生成器产生的值(通常为整数)的顺序。ed25519.NewKeyFromSeed()
)不应使用受污染参数进行调用。这样做可使攻击者控制作为伪随机数值生成器种子的值,然后可以预测由调用伪随机数值生成器产生的值的顺序。Random.setSeed()
不应使用受污染的整数参数进行调用。这样做可使攻击者控制作为伪随机数值生成器种子的值,因此能够预测由调用 Random.nextInt()
、Random.nextShort()
、Random.nextLong()
产生的、或 Random.nextBoolean()
返回的、或在 Random.nextBytes(byte[])
中设置的值(通常为整数)的顺序。Random.setSeed()
不应使用受污染的整数参数进行调用。这样做可使攻击者控制作为伪随机数值生成器种子的值,因此能够预测由调用 Random.nextInt()
、Random.nextLong()
、Random.nextDouble()
产生的、或 Random.nextBoolean()
返回的、或在 Random.nextBytes(ByteArray)
中设置的值(通常为整数)的顺序。random.randint()
);不应使用受污染参数进行调用。否则攻击者可以控制用作伪随机数生成器种子的值,从而能够预测由伪随机数生成器调用产生的值(通常为整数)的顺序。Random.setSeed()
不应使用受污染的整数参数进行调用。这样做可使攻击者控制作为伪随机数值生成器种子的值,因此能够预测由调用 Random.nextInt()
、Random.nextShort()
、Random.nextLong()
产生的、或 Random.nextBoolean()
返回的、或在 Random.nextBytes(byte[])
中设置的值(通常为整数)的顺序。
...
srand (time(NULL));
r = (rand() % 6) + 1;
...
...
import time
import random
random.seed(time.time())
...
createSocket()
时不会执行主机名验证。 getInsecure()
时会返回一个 SSL 套接字代理,其所有 SSL 检查会被禁用。https://safesecureserver.banking.com
时会随时接受颁发给 https://hackedserver.banking.com
的证书。此时,当服务器被黑客攻击发生 SSL 连接中断时,应用程序可能会泄漏用户敏感信息。