如何使用SSL/TLS和/或消息级安全保护RESTful php web服务

如何使用SSL/TLS和/或消息级安全保护RESTful php web服务,php,security,service,ssl,Php,Security,Service,Ssl,我有一个用php编写的RESTful web服务,它使用JSON进行通信。传输的一些数据非常敏感(密码),我正在寻找一种方法来实现服务的合理安全级别。客户端是silverlight 4应用程序 我一直在寻找关于如何实现SSL/TLS(我假设客户机证书验证属于这一类?)和消息级安全性的明确信息,但我找不到关于在php+json web服务中实际实现这些安全措施的好例子。如能提供任何信息和实例,我将不胜感激。我知道的原则,我只是不是很有经验的php。 目前,我拥有的唯一安全措施是一个非常基本的身份验

我有一个用php编写的RESTful web服务,它使用JSON进行通信。传输的一些数据非常敏感(密码),我正在寻找一种方法来实现服务的合理安全级别。客户端是silverlight 4应用程序

我一直在寻找关于如何实现SSL/TLS(我假设客户机证书验证属于这一类?)和消息级安全性的明确信息,但我找不到关于在php+json web服务中实际实现这些安全措施的好例子。如能提供任何信息和实例,我将不胜感激。我知道的原则,我只是不是很有经验的php。 目前,我拥有的唯一安全措施是一个非常基本的身份验证令牌系统,该系统在成功登录后创建服务器端会话,并为用户提供身份验证令牌,以便进行任何进一步的通信(直到会话过期或用户从不同的IP连接)。我真的想至少保护敏感流量,如密码

最后,在实现TLS和消息层安全(如漏洞和漏洞攻击)后,我必须注意哪些安全问题


先谢谢你

这对于您的情况来说可能太简单了,因为我对Silverlight一无所知,但是为您的Web API获取SSL证书怎么样?与制作API时一样,API只能通过https://协议而不是http://访问。这将加密客户端和服务器之间传输的任何内容。

您应该已经使用SSL来建立身份验证

然后,您可以使用身份验证后获得的相同令牌作为您的秘密散列来来回加密/解密该连接的数据,直到它变得无效


如果系统已正确锁定(内部),如果需要更高的速度,您可以跳过SSL进行加密数据传输(只要原始令牌是通过SSL生成的,并且系统知道令牌分配给哪个IP/etc).

假设您使用SSL/TLS正确配置了HTTPS,那么您主要关心的是如何为RESTful服务实现身份验证。由于HTTPS将使用SSL/TLS来加密客户端和服务器之间的通信,因此您不必担心加密。如果您需要了解如何正确配置SSL/TLS,请阅读

和中已经讨论了保护RESTful服务的最佳实践

总之,本文讨论了3种选择

  • HTTPS上的HTTP基本身份验证
  • Cookies和会话管理
  • 使用附加签名参数查询身份验证

另一个选择是探索OAuth2进行身份验证。如果是这样,您可以在

中很好地理解Oauth2,据我所知,您已经准备好了一个现有的代码。 为了简化,我将向您展示一个简单的示例,以及如何使用下面的代码。 请随意使用您需要的部件(这应该是非常直接的)

您当前创建应用程序的方式与服务器端会话配合得很好

在下面的代码下,我将包含一些更多的解释和到资源的链接,这将帮助您更好地理解代码,测试和调试您的应用程序

$Web\u服务\u URL=”https://website.tld/webservice.lang?wsdl';
$debug=false;
$proto='https';//e、 g.str'https'
$agent='Mozilla/5.0(WindowsNT6.3;rv:36.0)Gecko/20100101 Firefox/36.0';
$download=false;//只是打个电话,什么也不取,设置为false
//$download='/location/my_file.html';要获取内容并保存到文件,请设置文件位置
//初始化cURL会话
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$Web_服务_URL);
/** 
* 
*开始修复SSLv3/TLS连接问题
* 
*CURLOPT_SSL_VERIFYHOST和CURLOPT_SSL_VERIFYPEER可防止MITM攻击
*警告:禁用此选项将防止curl检测到中间人(MITM)攻击
* 
*/
/**
*@param CURLOPT_SSL_VERIFYPEER
* 
*FALSE停止CURL验证对等方的证书。
*可以使用CURLOPT_CAINFO选项指定要验证的备用证书,也可以使用CURLOPT_CAPATH选项指定证书目录。
*如果禁用了CURLOPT_SSL_VERIFYPEER(默认为2),则CURLOPT_SSL_VERIFYHOST也可能需要为TRUE或FALSE。
*将CURLOPT_SSL_VERIFYHOST设置为2(这是默认值)将确保提供给您的证书具有与您用于访问远程资源的URN匹配的“通用名称”。
*这是一个健康检查,但不能保证你的计划不会被取消。
* 
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,true);
/**
*@param CURLOPT_详细
*将on/off参数设置为1,使库在此句柄上显示有关其操作的大量详细信息。
*对于libcurl和/或协议调试和理解非常有用。详细信息将发送到stderr,
*或使用CURLOPT_STDERR设置的流。
*您几乎不希望在生产中使用此集合,在调试/报告问题时,您几乎总是希望使用此集合。
*/ 
curl_setopt($ch,CURLOPT_VERBOSE,$debug);
/**
*  
*@param CURLOPT_SSL_VERIFYHOST
* 
*检查SSL对等证书中是否存在公共名称。
*检查公共名称是否存在,并验证它是否与提供的主机名匹配。
* 
*@value 1检查SSL对等证书中是否存在公共名称。
*@value 2检查是否存在公共名称,并验证其是否与提供的主机名匹配。
*在生产环境中,此选项的值应保持为2(默认值)。
*cURL 7.28.1中删除了对值1的支持
*/
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
/**
* 
*强制使用TLS
*
$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;