How CSRF generates tokens to prevent attacks
In the past, we talked about CSRF and the principle of CSRF attacks. This article describes how to prevent CSRF and the encryption principles and implementation examples behind Token Generation.
1. Token structure.
To prevent CSRF tools, tokens must be unique and contain specific function information, such as the expiration timestamp.
The following figure describes the data structure of a token:
The data structure of the Token.
----------------------------------------------------------------------------- | msg | separator | signature | ----------------------------------------------------------------------------- | key | timestamp | . | Base64(sha256(msg)) | -----------------------------------------------------------------------------
Token consists of three parts: a). msg B). separator c). signature.
A). msg part: msg itself consists of two parts: one part, the subject of a random character, and the other part is the expiration timestamp.
B). separator: Separate the msg part with the symbol and the signature part generated after encryption. Here "." is used.
C). signature.
Signature is used to encrypt the msg mentioned above according to the msg Information Section mentioned in msg according to the specific secret lock.
Token = base64 (msg) format .. base64 (sha256 ("secret lock", msg ))
2. encryption of the Token. First, encrypt the data according to the appropriate encryption method. Here we use the sha256 hash algorithm and convert the BASE64 format. Then, we need to implicitly set the expiration time in the token string. On Demand, each token that interacts with the server has an expiration time that exceeds the time range, it is invalid and needs to be retrieved from the server again.
3. Verify the Token.
When a user obtains a token from the client and submits it to the server again, the server must determine the validity of the token. Otherwise, data can be directly processed without further judgment, and the token generation will be meaningless.
The verification process is:
A). Unpack the token.
Break down the received tokens first. "." Is the separator and is divided into msg part + signature part.
B) Compare the signature.
Perform base64 decoding on the msg part, decode_base64 (msg), and then perform the same encode_base64 (sha256 (msg) encryption on the decoded msg plain text. The secret lock is the same, and then the encrypted data is determined to be consistent with the token. signature part passed by the client. If they are consistent, the token is valid.
C) Determine the expiration time. If the token is valid, retrieve msg. timestamp and compare it with the current system time. If the expiration time is earlier than the current time, the token is expired and you need to obtain the token again.
The principles are common. Here we use lua to describe the processing process.
Local gen_token = function (key, expires) -- returns an expiration timestamp. If expires = nil then expires = OS. time () + 60 + 60*8 end -- base64-encode the msg part. Local msg = encode_base64 (json. encode ({key = key, expires = expires}) -- hash sha256. Local signature = encode_base64 (hmac_sha256 ('testkey', msg) -- concatenate a token. Return msg .. ". ".. signatureendlocal val_token = function (key, token) -- if not (token) then return nil, 'mssing csrf token' end -- msg of token, the signature part is split. Local msg, sig = token: match ("^ (. *) %. (. *) $ ") if not (msg) then return nil," malformed csrf token "end -- after unpacking, msg follows the same Encrypted key:" testkey ", re-Hash sha256 to compare signature. -- if they are inconsistent, the data in the token is faulty and the token is invalid. If not (sig = hmac_sha256 ('testkey', msg) then return nil, "invalid csrf token (bad sig)" end -- base64 Decoding of msg, determine whether the key is consistent with the input key. -- If they are inconsistent, the token is invalid. Msg = json. decode (decode_base64 (msg) if not (msg. key = key) then return nil, "invalid csrf token (bad key)" end -- Obtain the timestamp of the msg part to determine whether it is greater than the current time. If it is greater, the token expiration is invalid. If not (not msg. expires or msg. expires> OS. time () then return nil, "csrf token expired" endend
The following describes the Lua language encryption library. The lua language is different from other languages and does not agree to the official designated encryption library. In order to facilitate readers, the following describes the lua encrypted library. Lua is a weak language that is simple and clear. It is easy to describe certain topics and is similar to a pseudo language. It is also easy to operate and is easy to think about algorithms. Even if it is not applicable to lua later, it can be easily migrated to other languages.
In our development work, we will inevitably encrypt some data, and the use of encryption modules is essential. Many lua encryption libraries are listed in lua's official WIKI list, some of which are written in pure lua and use lua to call the C program for encryption. However, sometimes it takes some time to select these databases, but you just need to test whether encryption is useful. This is a list of lua organizations.
http://lua-users.org/wiki/CryptographyStuff
Let's explain why encryption is required. What are the tasks we are facing! The task we are facing now is to encrypt a string using the sha256 algorithm.
We selected several packages that support sha256 encryption from the list, and described these sdks.
1. SecureHashAlgorithm and SecureHashAlgorithmBW
This toolkit supports sha256 encryption and implements the pure lua method. The problem is that the two packages depend on lua5.2 and lua5.3 respectively.
The operating environment of our system is lua5.1, because most of the production environments are lua5.1, which cannot be changed for the time being due to historical reasons. If you want to port the 5.2 program to 5.1 for running, you also need to port a unique package that is unique to lua5.2. This is a part that is only available after lua5.2 upgrade: bit32, in lua5.3, this part is removed, and the porting power is not great. This package is not used for the time being.
2. Lcrypt
This package is not implemented by pure lua. The underlying encryption uses the C language and relies on another tool kit, libTomCrypt and libTomMath. The official websites of these two packages have been harmonized, the source code is available on github, so to make this package run properly, you need to manually install three source code projects by using make. If you have time, install the software for testing.
Website:
http://www.eder.us/projects/lcrypt/
3. LuaCrypto
The installation of this package uses luarocks, which is relatively simple.
luarocks install luacrypto
We use this package for encryption.
LuaCrypto is actually a front-end lua call of the openssl library. It depends on openssl. The openssl library obviously supports sha256 encryption, which is more reliable than other third-party implementations.
Write a simple encryption program:
local crypto = require("crypto")local hmac = require("crypto.hmac")local ret = hmac.digest("sha256", "abcdefg", "hmackey")print(ret)
The returned result of ret is the following string.
704d25d116a700656bfa5a6a7b0f462efdc7df828cdbafa6fbf8b39a12e83f24
We need to transform the code. When digest is called, the output format is specified as raw binary data and then encoded as base64 data.
local ret = hmac.digest("sha256", "abcdefg", "hmackey",rawequal) print(ret)
At this time, the output result is:
cE0l0RanAGVr+lpqew9GLv3H34KM26+m+/izmhLoPyQ= lua-base64
The following library is used, and the lua library is like this. Many multi-functional programs have many implementations and many unofficial third-party implementations.
https://github.com/toastdriven/lua-base64