我有一个用PHP编写的RESTful Web服务,它使用JSON进行通信。传输的一些数据非常敏感(密码),我正在寻找一种方法来实现服务的合理安全级别。客户端是silverlight 4应用程序。
我一直在搜索有关如何实现SSL / TLS的明确信息(我假设客户端证书身份验证属于该类别?)和消息级安全性,但我找不到关于这些安全措施的实际实现的良好示例一个php + json的网络服务。我将非常感谢任何信息和实际例子。我知道原理,我对php不是很有经验。 目前我唯一的安全措施是一个非常基本的身份验证令牌系统,在成功登录后创建服务器端会话并为用户提供身份验证令牌以进行任何进一步的通信(直到会话到期或用户从不同的IP)。我真的想要至少保护敏感的流量,比如密码。
最后,在实施TLS和消息层安全性之后,我需要注意哪些安全问题,如漏洞和漏洞?
提前谢谢你。
答案 0 :(得分:5)
假设您已使用SSL / TLS正确配置了HTTPS,那么您主要关注的是如何为RESTful服务实施身份验证。由于HTTPS将使用SSL / TLS加密客户端和服务器之间的通信加密不是您应该担心的事情。如果您需要了解如何正确配置SSL / TLS,请阅读Understanding SSL/TLS
RESTful Authentication和Best Practices for securing a REST API / web service中已讨论了保护RESTful服务的最佳做法。
总结一下,它讨论了3个选项
另一种选择是探索OAuth2进行身份验证。如果是这样,您可以在Beginner’s Guide to OAuth Part III : Security Architecture
中更好地了解Oauth2答案 1 :(得分:2)
您应该已经使用SSL来建立身份验证。
然后,您可以使用身份验证后获得的相同令牌作为您的秘密哈希来为该连接来回加密/解密数据,直到它变为无效。
如果系统被正确锁定(内部),您可以跳过SSL以进行加密数据传输(如果您需要更快的速度)(只要通过SSL生成原始令牌,并且系统知道令牌分配给/等的IP)
答案 2 :(得分:0)
这可能对您的情况来说太基本了,因为我对Silverlight一无所知,但是如何为您的Web API获取SSL证书呢?与制作API时一样,只能通过https://协议而不是http://访问 。这将加密客户端和服务器之间传输的任何内容。
答案 3 :(得分:0)
据我所知,您已有现有代码。 为了方便起见,我将向您展示一个简单的示例,以及如何使用下面的代码。 您可以随意使用您需要的部件(这应该非常简单)。
您当前创建应用的方式与服务器端会话一样正常。
根据下面的代码,我将提供更多解释和资源链接,这将有助于您更好地理解代码,测试和调试您的应用。
$Web_Service_URL = 'https://website.tld/webservice.lang?wsdl';
$debug = false;
$proto = 'https'; // e.g. str 'https'
$agent = 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0';
$download = false; // just to make a call and fetch nothing set to false
//$download = '/location/my_file.html'; to fetch content and save to file set the file location
// Init the cURL session
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $Web_Service_URL);
/**
*
* Start Fix SSLv3/TLS connectivity problems
*
* CURLOPT_SSL_VERIFYHOST and CURLOPT_SSL_VERIFYPEER prevent MITM attacks
* WARNING: Disabling this would prevent curl from detecting Man-in-the-middle (MITM) attack
*
*/
/**
* @param CURLOPT_SSL_VERIFYPEER
*
* FALSE to stop CURL from verifying the peer's certificate.
* Alternate certificates to verify against can be specified with the CURLOPT_CAINFO option or a certificate directory can be specified with the CURLOPT_CAPATH option.
* CURLOPT_SSL_VERIFYHOST may also need to be TRUE or FALSE if CURLOPT_SSL_VERIFYPEER is disabled (it defaults to 2).
* Setting CURLOPT_SSL_VERIFYHOST to 2 (This is the default value) will garantee that the certificate being presented to you have a 'common name' matching the URN you are using to access the remote resource.
* This is a healthy check but it doesn't guarantee your program is not being decieved.
*
*/
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
/**
* @param CURLOPT_VERBOSE
* Set the on/off parameter to 1 to make the library display a lot of verbose information about its operations on this handle.
* Very useful for libcurl and/or protocol debugging and understanding. The verbose information will be sent to stderr,
* or the stream set with CURLOPT_STDERR.
* You hardly ever want this set in production use, you will almost always want this when you debug/report problems.
*/
curl_setopt($ch, CURLOPT_VERBOSE, $debug);
/**
*
* @param CURLOPT_SSL_VERIFYHOST
*
* Check the existence of a common name in the SSL peer certificate.
* Check the existence of a common name and also verify that it matches the hostname provided.
*
* @value 1 to check the existence of a common name in the SSL peer certificate.
* @value 2 to check the existence of a common name and also verify that it matches the hostname provided.
* In production environments the value of this option should be kept at 2 (default value).
* Support for value 1 removed in cURL 7.28.1
*/
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
/**
*
* Force use of TLS
*
*/
if($proto == 'https')
{
/**
*
* Let's explain the magic of comparing your TLS certificate to the verified CA Authorities and how does that affect MITM attacks
*
* Man in the middle (MITM)
* Your program could be misleaded into talking to another server instead. This can be achieved through several mechanisms, like dns or arp poisoning.
* The intruder can also self-sign a certificate with the same 'comon name' your program is expecting.
* The communication would still be encrypted but you would be giving away your secrets to an impostor.
* This kind of attack is called 'man-in-the-middle'
* Defeating the 'man-in-the-middle'
* We need to to verify the certificate being presented to us is good for real. We do this by comparing it against a certificate we reasonable* trust.
* If the remote resource is protected by a certificate issued by one of the main CA's like Verisign, GeoTrust et al, you can safely compare against Mozilla's CA certificate bundle,
* which you can get from http://curl.haxx.se/docs/caextract.html
*
*/
//TODO: If TLSv1_1 found insecure and/or unreliable change to TLSv1_1 or TLS1_2
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); // CURL_SSLVERSION_TLSv1_1; CURL_SSLVERSION_TLSv1_2
curl_setopt($ch, CURLOPT_HEADER, 0); // Don’t return the header, just the html
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
$crt = substr(__FILE__, 0, strrpos( __FILE__, '\\'))."\crt\cacert.crt"; // WIN
}
else {
$crt = str_replace('\\', '/', substr(__FILE__, 0, strrpos( __FILE__, '/')))."/crt/cacert.crt"; // *NIX
}
// The cert path is relative to this file
curl_setopt($ch, CURLOPT_CAINFO, $crt); // Set the location of the CA-bundle
/**
* Fix Error: 35 - Unknown SSL protocol error in connections
*
* Improve maximum forward secrecy
*/
// Please keep in mind that this list has been checked against the SSL Labs' WEAK ciphers list in 2014.
$arrayCiphers = array(
'DHE-RSA-AES256-SHA',
'DHE-DSS-AES256-SHA',
'AES256-SHA',
'ADH-AES256-SHA',
'KRB5-DES-CBC3-SHA',
'EDH-RSA-DES-CBC3-SHA',
'EDH-DSS-DES-CBC3-SHA',
'DHE-RSA-AES128-SHA',
'DHE-DSS-AES128-SHA',
'ADH-AES128-SHA',
'AES128-SHA',
'KRB5-DES-CBC-SHA',
'EDH-RSA-DES-CBC-SHA',
'EDH-DSS-DES-CBC-SHA:DES-CBC-SHA',
'EXP-KRB5-DES-CBC-SHA',
'EXP-EDH-RSA-DES-CBC-SHA',
'EXP-EDH-DSS-DES-CBC-SHA',
'EXP-DES-CBC-SHA'
);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, implode(':', $arrayCiphers));
}
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
if($debug == true)
{curl_setopt($ch, CURLOPT_HEADER, 1);} // Get HTTP Headers Code
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
// ini_set('user_agent', 'NameOfAgent (http://www.example.net)');
curl_setopt($ch, CURLOPT_USERAGENT, $agent);
/**
* DEBUG cURL Call
* Don't forget to uncomment the CURLOPT_HEADER'
*/
// Get HTTP Headers Code
// Show Http Header
if($debug == true)
{
echo "<pre>";
echo curl_getinfo($ch, CURLINFO_HTTP_CODE);
}
// TODO:Check if any cURL connection error occurred
// see http://php.net/manual/en/function.curl-errno.php
/**
if(curl_errno($ch))
{
echo 'Curl error: ' . curl_error($ch);
}
*/
// Send the request and check the response
if (($result = curl_exec($ch)) === FALSE) {
die('cURL error: '.curl_error($ch)."<br />");
} else {
//echo "Success!<br />";
}
/**
* @function cURL_GetInfo
* other debug info, if needed
*
* The var_dump output:
* array(26) {
* ["url"]=> string(61) "https://www.example.com"
* ["content_type"]=> string(24) "text/html; charset=UTF-8"
* ["http_code"]=> int(200)
* ["header_size"]=> int(2462)
* ["request_size"]=> int(493)
* ["filetime"]=> int(-1)
* ["ssl_verify_result"]=> int(0)
* ["redirect_count"]=> int(2)
* ["total_time"]=> float(0.286363)
* ["namelookup_time"]=> float(7.1E-5)
* ["connect_time"]=> float(0.011754)
* ["pretransfer_time"]=> float(0.082954)
* ["size_upload"]=> float(0)
* ["size_download"]=> float(119772)
* ["speed_download"]=> float(418252)
* ["speed_upload"]=> float(0)
* ["download_content_length"]=> float(262)
* ["upload_content_length"]=> float(0)
* ["starttransfer_time"]=> float(0.156201)
* ["redirect_time"]=> float(0.076769)
* ["certinfo"]=> array(0) { }
* ["primary_ip"]=> string(14) "xxx.xxx.xxx.xxx."
* ["primary_port"]=> int(443)
* ["local_ip"]=> string(12) "192.168.0.15"
* ["local_port"]=> int(54606)
* ["redirect_url"]=> string(0) ""
* }
*/
$info = curl_getinfo($ch);
$arrCodes = array(
"client_error" => array("400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "410", "411", "412", "413", "414", "415", "416", "417"),
"server_error" => array("500", "502", "503", "504", "505")
);
// Return the error code, if any and exit
if(in_multi_array($info['http_code'], $arrCodes))
{
file_put_contents("logs/dberror.log", "Date: " . date('M j Y - G:i:s') . " --- Error: " . $info['http_code'].' URL: '.$info['url'].PHP_EOL, FILE_APPEND);
return $info['http_code']; exit;
}
curl_close($ch);
// If download is defined download to the specified file
if($download!= false)
{
$f = fopen($download, "w");
fwrite($f, $result);
fclose($f);
echo 'Web content downloaded to a file';
}
echo $result;
正如您在代码中看到的,您可以定义多个安全密码,但只能定义一个SSL版本参数。我不会早于TLS 1.1使用任何东西。任何早期的SSL版本都容易受到攻击。从最安全的TLS 1.2开始并测试,如果您的应用程序正在运行(通常,您应该没有任何问题。如果您遇到任何连接问题,请尝试TLS 1.1。 TLS 1.1版也很脆弱,唯一安全的(现在,直到他们发现一些漏洞)是TLS 1.2。
如果安全性是最重要的,请使用最高的可用TLS版本(TLS1.2)。当存在服务提供商安全责任时,客户端兼容性不是您的问题。
要查看的其他一些cURL参数:
根据强大的Qualys SSL Labs列表(2014)检查密码,并删除弱密码。随意添加/删除任何密码。
漏洞和攻击:Longjam,FREAK,POODLE,你的名字!谁知道其他攻击或漏洞未被发现?是!它们都会影响您选择的SSL / TLS连接。
可以在官方cURL页面找到可能的CURLOPT_SSLVERSION选项:http://curl.haxx.se/libcurl/c/CURLOPT_SSLVERSION.html
这也是你的应用程序周围很好的OWASP guide for creating a secure layer。
OWASP和Qualys SSL实验室是很好的资源。我甚至会对cURL和OpenSSL进行一些研究,以熟悉弱点,可能的安全选项和最佳实践。
有一些安全点,我没有提及也缺少,但我们无法涵盖所有内容。
如果您有任何疑问,我会随时回答,如果可以的话。