Services are always attacked, how to design a relatively secure interface access strategy?

Hello everyone, I'm Pharaoh. I recently took over a project. I was excited to do a big job, but I was almost stupid after a penetration test. Anti-replay attacks, request body tampering, and unauthorized attacks are all sorted out. Well, I don't know how this project has spent the past six months.

image.png

I don't know how everyone thinks about interface security, but for products that provide services to the public network, this can be said to be fatal. So, how to design a more secure interface access policy?

1. Token and signature

Generally, in the design process of client and server, most of them are divided into stateful and stateless interfaces. Generally, when a user is logged in, whether the user has permission or can request the interface is controlled by the token granted by the server after the user logs in successfully. But it does not mean that with the token, the request is safe, so what if the token is leaked? Can anyone call my service?

Just like going to the bank to withdraw money, you need a bank card. But just because you accidentally lost your bank card, the person who finds it can take your bank card to the bank for business. They also need to verify your identity, and you also need to know your bank card PIN.

Therefore, the token is only the credentials of the user authority and the session. In addition to the credentials of the session, we also need to verify the legitimacy of the request to prevent the loss of the client due to the leakage of the token.

The signature digest calculation is used to verify the validity of the request. Students often confuse the two. It is safe to use tokens.

2. Signature calculation design

The calculation of the signature digest is generally divided into the signature value and the signature key. The signature generation method is as follows:

signature = Base64(HMAC-SHA256(LOWER(MD5(key)), StringToSign))

2.1 Design of Signature String

When designing the signature string, we need to think about what kind of request headers we need to design for different attack strategies.

Commonly used signature strings are designed as follows:

StringToSign =  Content-MD5 + "\n"
               + CanonicalizedHeaders

The CanonicalizedHeaders construction method is as follows:

  1. Headers prefixed with service, but not including service-signature, as follows
  • service-nonce: The client generates a 32-bit random string, which cannot be repeated for all clients within 5 minutes. When the platform repeats the nonce, the client needs to re-request.
  • service-date: The time when the request is generated is more than 5 minutes away from the server's local time, and the authentication fails.
  • service-session-id:客户端会话id,用于本次登录后的所有请求会话标识。
  • service-client:客户端信息,包括客户端类型、客户端版本、操作系统等。
  1. Header名称全部小写,值前后应不包含空格
  2. Header的名称和值之间用“:”相隔,组成一个完整的header
  3. 根据header名称的字符顺序,将header从小到大进行字典排序
    每个header之后跟一个“\n”

StringToSign生成示例

eB5eJF1ptWaXm4bijSPyxw==\n
service-client:ewogICAgImRhdGEiOiB7CiAgICAgICAgImNsaWVudFR5cGUiOiAieHh4IiwKICAgICAgICAiY2xpZW50VmVyc2lvbiI6ICIzLjAiCiAgICB9Cn0=\n
service-date:2022-07-22T14:43:07Z\n
service-nonce:d36e316282959a9ed4c89851497a717f\n
service-session-id:0123456\n

2.3. 签名key设计

一般客户端请求的接口类型有三种,分别是有登录状态和无登录状态以及登录这个特殊接口。无登录状态前的接口都是服务为了提供能力而做的一些接口,相比而言都是提供通用能力的。不涉及客户个人信息相关。安全风险较小。用户登录后的接口一般都是涉及到客户信息的接口,隐私泄露风险较大。因此对于每一种状态可采用不同的key值设计。讲风险降到最低。

2.3.1. 用户未登录签名key值

用户未登录时,平台提供通用能力,调用服务端接口时使用约定好的固定SK进行接口签名鉴权即可。固定SK由后台提供16位的随机字符串。

2.3.2. 用户登录签名key值

我们知道,所有在客户端和前端保存的key值永远不是最安全的,有可能被拆包而发现对应的加密SK,从而被不法分子破解,因此用户登录时,传输密码时,如果使用固定key,有可能body体被解开,密码被泄露的风险。

登录时用户会输入密码,而服务端也知道用户加密后的密码,那么使用用户输入的密码当做key则是最安全的方式。并且交互过程中不需要将用户密码放在body体重传到后端进行校验,只需校验签名的准确性即可。这样就可以极大的增加用户密码的安全性。

用户登录验证时会输入用户的密码,则登录时用户密码作为签名的key进行鉴权校验。加密key为:SHA256(LOWER(MD5(passwd)),salt),salt为用户的盐值,可以使用用户的手机号。

2.3.3. 用户登录签名key值

After a user logs in successfully, all interfaces must be authenticated. For the security of each user, each user is issued their own SK, which is obtained after the login is successful. In this way, the client saves the sercet in memory, which can effectively prevent SK leakage.

1. After the user is successfully registered, a 16-bit sercet is generated for the user account in the background

2. After the user logs in successfully, the background returns the user's sercet

3. The interface authentication after login uses sercet for authentication.

3. Interface validity verification process

The request validity verification process is as follows:

image.png

1. First, check whether the nonce of the request header of the interface is repeated within 5 minutes, which can effectively prevent replay attacks

2. Then verify the timestamp to prevent client time tampering attacks

3. After that, verify the MD5 of the request to prevent tampering with the request body.

4. Finally, perform combined signature verification on nonce, date, md5 and session to verify whether the signature value is successful. It can effectively prevent the problem that the above single modification verification passes, but the signature value verification fails.

Based on the above verification, most attack scenarios can be basically prevented. Of course, in order to be more secure, you can also add security designs to protect users' property, such as black and white list restrictions, interface access current restrictions, user common device binding, and user remote login.

Well, I wonder if everyone in this article has a deeper understanding of security design? Do you have a better interface security design solution, you can chat together.

I am Wang Laoshi, an engineering lion with ideas and connotations. Follow me and learn more technical knowledge.

Guess you like

Origin juejin.im/post/7121168651222253599