C # How to Prevent replay attacks,
Replay attacks
A replay attack means that hackers capture packets to obtain client request data and request connections, repeatedly sending requests to the server. For example, you have a "buy" operation. When you click the Buy button, you can send a purchase request to the server. At this time, the hacker captured your request and obtained your data transmission. Because all the data you fill in is real and valid and can be purchased successfully, he does not need to make any changes, just submit your data to the server again. This leads to the fact that you only want to buy one product. As a result, hackers replay the attack and you have bought it multiple times. If it is a user operation, it will surely be inexplicable: How can I buy so many of the same products? Therefore, replay attacks are very harmful, especially when it comes to money transactions. Therefore, anti-replay attacks are essential for e-commerce projects.
Solution
Timestamp (tamp) + sign (sign ). That is to say, when each request is sent, two parameters are passed, tamp and sign. For example
Original request: http: // 127.0.0.1/api/buyproduct
Changed to http: // 127.0.0.1/api/buyproduct? Tamp = 1403149835 & sign = 945bf36r046bd84df2985ad625c9f92415eccd1w
The purpose of digital signature is to ensure the validity of the request. Because the signature is encrypted, only the client and server know the encryption method and Key, so the third party cannot simulate it. We determine the validity of the request by verifying the sign. If the sign verification fails, the request is regarded as invalid. Otherwise, the request is valid. However, digital signatures cannot prevent replay attacks because hackers can capture your tamp and sign (without any modification) and then send requests. At this time, it is necessary to verify the timestamp.
The timestamp is used to ensure the timeliness of requests. We store the timestamp of the last request and compare the timestamp of the next request. If the request timestamp is the same as or less than the previous timestamp, the request is regarded as an outdated request and is invalid. Under normal circumstances, the second request must be later than the previous time, and cannot be equal or less.
Someone may ask, why do I need a digital signature if I just use a timestamp? Because hackers may capture requests and modify the timestamp to a valid timestamp value. Our Digital Signature adopts tamp + key combination encryption. Even if the hacker modifies the tamp, but the hacker does not know the key, the sign verification step successfully blocks the hacker's request.
Instance code
The SHA1 and SHA1 encryption methods are encapsulated and written as string extension methods.
/// <Summary> /// digital signature /// </summary> /// <param name = "tamp"> timestamp (imported by the client) </param> /// <param name = "key"> Key </param> /// <returns> </returns> private string Sign (string tamp, string key) {string txt = tamp + "|" + key; // Add "|" to each parameter to increase the complexity of string sign = txt. getSha1 (); return sign ;}
Verification Method: the client-side encryption method is the same as the server-side encryption method. If the two encryption results are inconsistent, verification fails. If the client is js, you must confuse the js Code and disable right-click. Because I use the Session to store the timestamp of the last request, and the Session will be outdated, when the Session is attacked from time to time, it will win, therefore, the request is valid for 30 seconds.
/// <Summary> /// check whether the request is valid, anti-replay /// </summary> /// <param name = "tamp"> timestamp (transmitted by the client) </param> /// <param name = "key"> Key </param> /// <param name = "sign"> signature (transmitted by the client) </param> // <returns> </returns> private bool CheckRequest (string tamp, string key, string sign) {// verify the signature (compare the encrypted result of the client with the encrypted result of the server, if not, the signature fails) if (sign. toUpper ()! = Sign (tamp, key ). toUpper () return false; // obtain the current timestamp DateTime DateStart = new DateTime (1970, 1, 1, 8, 0, 0); int nowTamp = Convert. toInt32 (DateTime. now-DateStart ). totalSeconds );
If (nowTamp-int. Parse (tamp)> 30) return false; // The request validity period is limited to 30 seconds because the Session may be outdated.
// Obtain the last timestamp string prevTamp = Session ["tamp"] as string;
// Determines whether it is null. if it is null, it indicates that it is the first request if (! String. isNullOrWhiteSpace (prevTamp) {if (int. parse (tamp)> int. parse (prevTamp) {Session ["tamp"] = tamp; return true;} else {return false ;}} else {Session ["tamp"] = tamp; return true ;}}