《细说PHP》第四版 样章 第23章 自定义PHP接口规范 5

23.3  接口的安全控制规范

23.2节的示例实现了一个简单接口,但是这个接口此时是在“裸奔”的。因为这个接口所有人都可以请求,不仅我们的客户端可以正常访问数据,如果有人使用如fiddler、wireshark等抓包工具,就很容易获取这个API地址,可以随意地请求获取或篡改我们的数据,这很显然是不安全的。因此,在设计接口时必须加上安全控制这一环节。

 

23.3.1  API安全控制原则

由于Web API是基于互联网的应用,因此对安全性的要求远比在本地访问数据库严格得多。一般通用的做法是,采用参数加密签名方式传递,即在传递参数时,增加一个加密签名,在服务器端验证签名内容,防止被篡改。对一般的接口访问,只需要使用用户身份的token进行校验,只有检查通过才允许访问数据。API常用的安全控制原则有以下几种:

(1)使用用户名密码。这种方式比较简单,可以有效识别用户的身份(如用户信息、密码,或者相关的接口权限等)。验证成功后,返回相关的数据。

(2)使用安全签名。这种方式提交的数据,URL的连接参数是要经过一定规则的安全加密的,服务器收到数据后也经过同样的规则进行安全加密,确认数据没有被中途篡改后,再进行数据的修改处理。因此,我们可以为不同客户端,如Web、App等不同接入方式指定不同的密钥,但是密钥是双方约定的,并不在网络连接上传输,连接传输的一般是接入的“key”,服务器通过这个“key”来进行签名参数的加密对比。目前,微信后台的回调处理机制,采用的就是这种方法。

(3)公开的接口调用,不需要传入用户令牌,或者对参数进行加密签名,这种接口一般较少,只是提供一些很常规的数据显示而已。

 

23.3.2  API安全控制简单实现步骤

API的安全控制方法有很多,可以根据项目自身的情况定制一些方法,也可以借鉴一些大的平台处理接口的算法。本节通过一些简单的控制方式,来一步步实现API的安全访问控制。

1.增加时间戳参数

首先,我们在API的URL中添加一个时间戳参数,如“timestamp”,要求请求的客户端在请求接口的时候必须添加此参数。如果在请求的时候没有该参数,就不返回数据。另外,通过时间戳参数,也可以限制请求接口必须要在某个时间段内完成,即便有人发现了接口地址,也只能使用一段时间。加入时间戳参数后,请求接口的URL地址格式如下所示:

 

https://localhost/userapi.php?id=1&timestamp=1519552181

   

 

修改上例中的userclient.php文件,分别使用get()方法请求两次userapi.php接口,一次在URL加上“timestamp”参数,一次不加该参数。修改后代码片段如下所示:

b50e061b719046a6888540f94603f674.png

同样地,修改userapi.php接口文件,判断在请求接口时,请求的URL中是否带有“timestamp”参数,并且限制该URL只能在5分钟内有效。修改后的代码片段如下所示:

04a9b07159e84289870dec82f9ea6147.png

运行的结果如图23-8所示。

8cad9dd59f0d49578d511ea668c9dffa.png

图23-8  测试请求API的URL参数带有时间戳的结果

 

虽然我们实现了客户端软件在请求API时需要添加“timestamp”参数才能获取数据,但这样依然不能防止别人获取我们的数据,因为通过抓包工具依然是可以看到地址的,所以别人也可以添加 “timestamp”参数请求我们的接口。限时访问也只能瞒得过一般的程序员,稍微细心的程序员就会发现这个规律,他可以生成当前的时间戳,然后模拟参数发送请求来获取数据。

 

2. 增加签名参数

在发送API调用请求时,为了确保客户端应用与API服务器之间的安全通信,防止盗用URL、数据篡改等恶意攻击行为,在API验证规则中可以使用参数签名机制。过程是客户端应用在调用API之前,需要通过算法计算一个加密的签名,并追加到请求参数中,参数名可以为“sign”。API服务器在接收到请求时,使用同样的算法重新计算签名,并判断其值是否与应用传递来的“sign”参数值一致,以此判定当前API调用请求是否是被第三方伪造或篡改的。

签名的算法很多,本节模拟支付宝的签名算法。例如,制定一个规则,将所有URL的参数提取出来,然后根据参数名进行排序,再将排序后的数组拼接成字符串,最后对该字符串进行 md5或sha1加密(建议使用sha1)后得到“sign”。例如,当前我们的URL 如下所示:

 

http://localhost/userapi.php?id=1&timestamp=1527068730

 

(1)得到参数数组:['timestamp'=> 1527068730, 'id'=>100]。

(2)键名根据 ASCII 码进行排序后:['id'=>100,  'timestamp'=> 1527068730]。

(3)组合成字符串:id=100timestamp=1527068730。

(4)使用sha1()函数加密得到fd8cc3348652b9cbf2714689ab7ee9105da67cf4。

客户端和API服务器端签名的计算方法相同,计算后的请求URL地址如下所示:

 

http://localhost/userapi.php?id=1&timestamp=1527068730&sign=fd8cc3348652b9cbf2714689ab7ee9105da67cf4

   

继续修改上例中的userclient.php文件,再分别使用get()方法请求3次userapi.php接口,第一次没有添加“sign”参数,第二次使用错误的“sign”参数,第三次使用全部正确的参数。并通过上面的算法生成“sign”参数。修改后代码片段如下所示:

9e0aebb968934a4981c505f77b390e55.png

同样地,修改userapi.php接口文件,判断在请求接口时,请求的URL是否带有“sign”参数,和客户端使用相同的算法计算签名,并和URL中接收到的客户端“sign”参数进行匹配,如果相同则返回数据,如果不同则可能被篡改,返回错误消息。修改后代码片段如下所示:

46db409a066c4fd6a45596a41b65dc95.png

 

运行的结果如图23-9所示:

e1a32f8f699e42d2a2a0c18fc3ba4a26.png

图23-9  测试请求API的URL参数带有sign的结果

通过签名参数,能大大提高接口的安全性,其他人不能随意地请求接口。虽然有人也可以抓取接口地址,但是也只能获取这一条数据,不能请求别的数据。例如,有人抓取了上例中的这个接口地址,它只能获取ID为1的文章。并且5分钟的时间内,它无法通过修改参数来获取ID为100的文章,因为一旦参数变化,“sign”参数校验就会失败。支付宝接口就是这样做的,避免交易金额随便被更改。

 

3. 引入token

虽然通过添加“时间戳”和“签名”参数,接口相对比较安全了,但是还是有隐患,如果别人知道了加密规则(当然规则可以变化,如倒序排序,或双层sha1加密等),采用相同的规则加密参数得到“sign”,就能接着获取其他的数据了。所以,需要再引入另一个元素“token”,它是一个约定值,相当于“暗号”,其实就是只有客户端和服务器端知道的一个随机字符串。当然这个“token”字符串在客户端用户那里是不可见的,如果客户端是App,代码是编译过的,客户端使用PHP程序,在服务器上不会传给用户。引入“token”只需要将这个随机的字符串加入sign 的计算中,就能再次提高接口的安全性。 继续修改上例中的userclient.php文件,引入“token”进入签名运算。修改后代码片段如下所示:

0e5b6ff6d20d4358bebfca3c67933ec6.png

继续修改上例中的userapi.php文件,同样引入相同内容的“token”进入签名运算中。修改后代码片段如下所示:

eb2b54b48a014f6fa9b5a1bb4969aab2.png

再次运行后,和上例运行得到的结果相同。到此为止,我们的接口已经变得比较的安全了,其他人想要请求我们的接口,就必须知道我们的签名加密规则和随机的“token”字符串,这个可能性就太低了。

 

 

22e29d5e0ec74e7ca39f22b2d85d51cc.png

3899402b65b34a5089e992a43e99c599.png

6dcc0305119f47f0ab7107902cb398c2.png

6c5ca632e9194b29923630a1dbcbd407.png

转载于:https://my.oschina.net/u/4125915/blog/3101630

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第四步是在layout文件中使用Data Binding来绑定数据。下面是具体步骤: 1. 在layout文件的根布局中添加`<layout>`标签,使其变成Data Binding布局。 ``` <layout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- UI元素 --> </LinearLayout> </layout> ``` 2. 在需要绑定数据的UI元素中添加`android:id`属性,用于在ViewModel中引用。 ``` <TextView android:id="@+id/myTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> ``` 3. 在ViewModel中创建一个变量,用于引用UI元素。 ``` class MyViewModel : ViewModel() { val myText = MutableLiveData<String>() } ``` 4. 在Activity或Fragment中创建一个ViewModel实例,并将其与Data Binding绑定。 ``` class MyActivity : AppCompatActivity() { private lateinit var binding: MyLayoutBinding private val viewModel by viewModels<MyViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.my_layout) binding.lifecycleOwner = this binding.viewModel = viewModel // 更新UI viewModel.myText.value = "Hello World!" } } ``` 5. 在layout文件中使用Data Binding将ViewModel中的数据绑定到UI元素。 ``` <TextView android:id="@+id/myTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.myText}" /> ``` 这样就完成了在layout文件中使用Data Binding来绑定数据的步骤。注意要在Activity或Fragment中设置`lifecycleOwner`和`viewModel`,以便让Data Binding知道它们之间的关系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值