用php写后端怎么使用模板

Introduction

介绍

People have always sought ways to keep secrets from prying eyes or to send secret messages.  Since 499BC, when Histiaeus shaved the head of a slave and tattooed a message on his scalp, we have used steganography, encoding, and more recently, HTTPS protocol, hashing, and encryption to hide information  This article explores some of PHP's tools that help us keep our secrets.

人们一直在寻找各种方法,以防止他人窥视机密或发送机密信息。 自499BC年以来, Histiaeus剃了一个奴隶的头并在他的头皮上刻了一条消息,我们就开始使用隐写术 ,编码,最近使用HTTPS协议 ,哈希和加密来隐藏信息。本文探讨了一些PHP的工具,这些工具可以帮助我们保守我们的秘密。

Obfuscation

混淆

At the lowest level of secrecy, we can simply obscure the information.  The information is not really encoded differently, it's just made invisible.  Steganography ("covered writing") is an example of security by obscurity.  A message might be hidden in the metadata of a picture, for example in the IPTC information of an image.  When rendered in a browser window, the image would look like any other image, but when the IPTC information is parsed, the information can be revealed.  Another steganography technique can embed the message in the image itself, using a color that is so close to its background that it is not readily visible.  A single "signature pixel" containing this color is located at a predetermined x/y coordinate inside the image.  Using image processing software, it's possible to detect the color of this pixel and raise the brightness or change the color of all the pixels that exactly match the signature.  When the pixels are changed the message can be made visible.

在最低级别的保密性下,我们可以简单地隐藏信息。 信息的编码方式并没有真正不同,只是变得不可见。 隐写术(“隐秘文字”)是掩盖安全性的一个例子。 消息可能隐藏在图片的元数据中,例如隐藏在图片的IPTC信息中。 当在浏览器窗口中呈现图像时,该图像看起来与任何其他图像一样,但是当解析 IPTC信息时,可以显示该信息。 另一种隐写技术可以使用一种非常接近其背景的颜色将消息嵌入到图像本身中,从而使其不易被看到。 包含该颜色的单个“签名像素”位于图像内部的预定x / y坐标处。 使用图像处理软件,可以检测到该像素的颜色并提高亮度或更改与签名完全匹配的所有像素的颜色。 更改像素后,该消息将变为可见。

This image contains a secret message.  Most of the background is RGB = #006D8B, but the message text is RGB = #016D8B.  The color difference is likely to be imperceptible to the naked eye, even on a good computer monitor.

该图像包含一个秘密消息。 大多数背景是RGB =#006D8B,但是消息文本是RGB =#016D8B。 即使在好的计算机显示器上,肉眼也可能察觉不到色差。

When image processing software is applied to reveal the message, we get this image.

当应用图像处理软件来显示消息时,我们得到了这张图像。

Here is the script that demonstrates the way to reveal the hidden message.

这是演示隐藏信息的脚本。

<?php // demo/steganography_elephant.php
/*** Color-Sensitive Image Steganography*/
error_reporting(E_ALL);// ORIGINAL AND SOON-TO-BE-REVEALED IMAGE URLS
$url = 'images/steganography_elephant.png';
$new = 'images/revealed_text_elephant.png';// ACQUIRE THE ORIGINAL IMAGE WITH THE SECRET MESSAGE
$img = ImageCreateFromPNG($url);// LOCATE THE COLOR OF THE SIGNAL PIXEL
$rgb = ImageColorAt($img, 0, 0);// ALLOCATE BRIGHT YELLOW REPLACEMENT COLOR IDENTIFIER
$cid = ImageColorAllocate($img, 0xFF, 0xFF, 0x00);// GET PIXEL COUNTS FOR WIDTH AND HEIGHT OF IMAGE
$wide = ImagesX($img);
$high = ImagesY($img);// PIXEL POINTERS SKIP THE SIGNAL PIXEL
$nw = $nh = 1;// ITERATE OVER ALL OF THE PIXELS
while ($nw < $wide)
{while ($nh < $high){$pxl = ImageColorAt($img, $nw, $nh);// REPLACE THE SIGNAL PIXELS WITH THE REPLACEMENT COLORif ($pxl == $rgb){ImageSetPixel($img, $nw, $nh, $cid);}$nh++;}$nh = 0;$nw++;
}// WRITE THE NEW IMAGE FILE
ImagePNG($img, $new);
ImageDestroy($img);// SHOW THE TWO IMAGES
$original = '<img style="padding:4px" src="' . $url . '" />';
$revealed = '<img style="padding:4px" src="' . $new . '" />';
echo $original, $revealed;

One step up from covered writing is letter-scramble obfuscation.  PHP implements letter-scramble through the str_rot13() function.  ROT13 encoding shifts every letter by 13 places in the alphabet.  Since the English alphabet has 26 characters, the same function can be used to encode and decode strings.  Consider this code snippet and its output.

掩盖文字的第一步是对字母的混淆。 PHP通过str_rot13()函数实现字母加扰。 ROT13编码将每个字母在字母表中移动13位。 由于英文字母有26个字符,因此可以使用相同的功能来编码和解码字符串。 考虑一下此代码段及其输出。

<?php // demo/rot13.php
/*** Scramble Letters** http://php.net/manual/en/function.str-rot13.php* https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog*/
error_reporting(E_ALL);
echo '<pre>';// TEST DATA
$str = "The quick brown fox jumps over the lazy dog.";// ENCODE ONCE
$rot = str_rot13($str);// ENCODE AGAIN TO DECODE
$new = str_rot13($rot);// SHOW THE WORK PRODUCT
echo PHP_EOL . $str;
echo PHP_EOL . $rot;
echo PHP_EOL . $new;
The quick brown fox jumps over the lazy dog.
Gur dhvpx oebja sbk whzcf bire gur ynml qbt.
The quick brown fox jumps over the lazy dog.

ROT13 encoding is less useful if you have non-English letters.  It does not take much effort to understand and reverse ROT13 encoding.  All it's good for is providing momentary confusion, which might be enough to discourage prying eyes.  After all, what would you think if your message said, "Abj vf gur gvzr sbe nyy tbbq zra gb pbzr gb gur nvq bs gur cnegl?"

如果您使用非英文字母,则ROT13编码的用处不大。 理解和反转ROT13编码不需要太多的工作。 这样做的好处是暂时造成混乱,这足以阻止窥视。 毕竟,如果您的消息说“ Abj vf gur gvzr sbe nyy tbbq zra gb pbzr gb gur nvq bs gur cnegl”,您会怎么想?

But security by obscurity is not really good security at all.  Stronger measures are needed to protect information that really needs protection.

但是,默默无闻的安全根本不是真正的安全。 需要采取更强有力的措施来保护真正需要保护的信息。

Hashing vs Encryption

散列与加密

Most of the information we want to collect and disseminate is kept in clear text, but what about information like passwords or personally identifiable information?  These are things we want to keep from prying eyes.  Hashing and encryption are different things, and the differences matter in information protection schemes.  With hashing, the process consists of making a one-way coded representation of the original information.  The hash can be used to verify the original information (or any copy of the original information) but the hash cannot be reversed to get the original information.  With encryption, there is a two-way process.  Encrypted information can be used (usually with a secret key) to recover the original information.

我们希望收集和传播的大多数信息都以明文形式保存,但是诸如密码或个人身份信息之类的信息呢? 这些是我们要避免窥视的东西。 散列和加密是不同的事物,而差异在信息保护方案中至关重要。 对于散列,该过程包括对原始信息进行单向编码表示。 哈希可以用于验证原始信息(或原始信息的任何副本),但是不能将哈希

Hashing

散列

A common form of hashing is accomplished with the md5() function.  This function implements the RSA message digest algorithm.  It is idempotent, meaning that for any given input, there is one and only one associated message digest.  The message digest is 256 bits long, and this means that MD5 "collisions" are possible.  A collision occurs when two different source strings produce identical message digest strings.  Fortunately collisions are pretty rare outside of the laboratory.  Here is an example of md5() and its output.

哈希的一种常见形式是使用md5()函数完成的。 此功能实现RSA消息摘要算法。 它是幂等的,这意味着对于任何给定的输入,只有一个关联的消息摘要。 消息摘要的长度为256位,这意味着可能发生MD5“冲突”。 当两个不同的源字符串产生相同的消息摘要字符串时,就会发生冲突。 幸运的是,在实验室之外很少发生碰撞。 这是md5()及其输出的示例。

<?php // demo/md5.php
/*** Compute message digest** http://php.net/manual/en/function.md5.php* http://www.faqs.org/rfcs/rfc1321.html*/
error_reporting(E_ALL);
echo '<pre>';// TEST DATA
$str = "The quick brown fox jumps over the lazy dog.";// MAKE A HASH
$md5 = md5($str);// SHOW THE WORK PRODUCT
echo PHP_EOL . $str;
echo PHP_EOL . $md5;
The quick brown fox jumps over the lazy dog.
e4d909c290d0fb1ca068ffaddf22cbd0

The Decline and Fall of MD5

MD5的衰落

One of the criticisms of MD5, entirely valid, goes to the nature of its idempotence.  In the early days of hashing, idempotence was not really a problem because the difficulty of reversing the MD5 hash was substantial, even to the point of being beyond the limits of computability.  That is no longer the case, and it's not necessary to reverse the MD5 hash.  A "dictionary" or rainbow table attack is computationally trivial today.  In a dictionary attack, every possible word or phrase is fed to the md5() function, and the corresponding hash string is stored in a database.  When an attacker seeks to reverse the MD5 hash, it only takes a database lookup to find the original word or phrase.  The MD5 hash of password is always 5f4dcc3b5aa765d61d8327deb882cf99 no matter how many times it is hashed, and an attacker who finds 5f4dcc3b5aa765d61d8327deb882cf99 in a database, knows that the original string was password.

完全有效的对MD5的批评之一是它的幂等性 。 在哈希的早期,幂等性并不是真正的问题,因为反转MD5哈希的困难很大,甚至超过了可计算性的极限。 情况不再如此,也不必攻击或彩虹表攻击在计算上是微不足道的。 在字典攻击中,每个可能的单词或短语都被馈送到md5()函数,并且相应的哈希字符串存储在数据库中。 当攻击者寻求逆转MD5哈希值时,只需进行数据库查找即可找到原始单词或短语。

To counteract the predictable nature of idempotence, early efforts led us to add salt strings before making the md5() hash.  This helps some, but not enough to make the hashed strings cryptographically strong.  Some additional discussion on md5() is in this article (see About Storing Passwords).  The consensus today is that secrecy of MD5 hashing has been overtaken by technology.  If we want to keep a secret hash today we use PHP password hashing techniques.

为了抵消幂等的可预测性,早期的努力使我们在添加md5()哈希之前添加了盐字符串。 这可以帮助一些但不足以使散列字符串在密码学上更强。 本文中对md5()进行了一些其他讨论(请参阅PHP密码哈希技术 。

Where MD5 is Still Useful

MD5仍然有用的地方

Idempotence is not all bad, and is quite useful when we want to verify that a message has not been damaged in transmission.  We can take the md5() string of the message text and append it to the message document.  On the receiving end, the client can also take the md5() string of the message text and compare it to the md5() string at the end of the message document.  If there is a mismatch, the client can know that the message is damaged.  Try installing and running this little script.  If you change even one of the characters in the message, it will be detected.

幂等并不全都是坏事,当我们要验证消息在传输中是否未被损坏时,同等性非常有用。 我们可以采用消息文本的md5()字符串并将其附加到消息文档中。 在接收端,客户端还可以获取消息文本的md5()字符串,并将其与消息文档末尾的md5()字符串进行比较。 如果存在不匹配,则客户端可以知道消息已损坏。 尝试安装并运行这个小脚本。 如果您甚至更改了消息中的字符之一,它将被检测到。

<?php // demo/md5.php
/*** Compare message digest** http://php.net/manual/en/function.md5.php* http://www.faqs.org/rfcs/rfc1321.html*/
error_reporting(E_ALL);
echo '<pre>';// TEST DATA
$str = "The quick brown fox jumps over the lazy dog.";// MAKE A HASH
$md5 = md5($str);// TEST THE MESSAGE
if (!empty($_POST))
{// TEST FOR DAMAGED FIELDSif ( ($str != $_POST['str']) || ($md5 != $_POST['md5']) ){echo "Message is damaged";}else echo "Message is intact";
}// MAKE A FORM
$form = <<<EOF
<form method="post">
<input name="str" value="$str" size="48" />
<input name="md5" value="$md5" size="32" type="hidden" readonly />
<input type="submit" />
</form>
EOF;echo $form;

The md5() string is useful when creating HTTP cookies.  If you want to know when a cookie is intact, you can set the cookie with both the cookie value and the salted md5() string of the cookie value.  When the browser returns the cookie, you can recreate the salted md5() string and compare it to the contents of the cookie.  A mismatch tells you to disregard the cookie.  This little code snippet shows the praxis of the anti-tamper cookie.

创建HTTP cookie时,md5()字符串很有用。 如果您想知道cookie何时完整无缺,则可以同时使用cookie值和cookie值的盐腌md5()字符串来设置cookie。 当浏览器返回cookie时,您可以重新创建盐腌的md5()字符串,并将其与cookie的内容进行比较。 不匹配会告诉您忽略cookie。 这个小代码片段显示了实践的防篡改的cookie。

<?php // demo/cookie_safety.php
/*** Demonstrate how to encode information in a cookie in a way that* can reduce the risk of cookie tampering.  We do this with a* salted message digest that contains the contents of the cookie.** When the cookie is returned, we use the value to recreate the* salted message digest.  If the new message digest does not match* the message digest in the cookie, we can discard the cookie.** The cookie will be url-encoded on the browser like this:* MARY+HAD+A+LITTLE+LAMB%7Ccf783c37f18d007d23483b11759ec181* It will be url-decoded before it is presented to php.** http://php.net/manual/en/function.setcookie.php* http://php.net/manual/en/function.md5.php*/
error_reporting(E_ALL);
ob_start();
echo '<pre>';// A DATA DELIMITER
$dlm = '|';// YOUR OWN SECRET CODE IS A SALT FOR THE MESSAGE DIGEST STRING
$secret_code = 'MY SECRET';// A DATA STRING THAT WE WANT TO STORE (MIGHT BE A DB KEY)
$cookie_value = 'MARY HAD A LITTLE LAMB';// MAKE A MESSAGE DIGEST FROM THE DATA STRING TOGETHER WITH OUR SECRET CODE
$cookie_code = md5($cookie_value . $secret_code);// CONSTRUCT THE COOKIE STRING WITH THE CLEAR TEXT AND THE CODED STRING
$safe_cookie_value = $cookie_value . $dlm . $cookie_code;// SET THE COOKIE LIKE "MARY HAD A LITTLE LAMB|cf783c37f18d007d23483b11759ec181"
setcookie
( 'safe_cookie'         // COOKIE NAME
, $safe_cookie_value    // COOKIE VALUE
, NULL                  // COOKIE EXPIRATION
, NULL                  // COOKIE PATH
, NULL                  // COOKIE DOMAIN/SUBDOMAIN http://php.net/manual/en/function.setcookie.php#76395
, NULL                  // COOKIE SECURE (HTTPS)
, NULL                  // COOKIE HTTP-ONLY
)
;// HOW TO TEST THE COOKIE
if (isset($_COOKIE["safe_cookie"]))
{// BREAK THE COOKIE VALUE APART AT THE DELIMITER$array = explode($dlm, $_COOKIE["safe_cookie"]);// ENCODE THE DATA STRING TOGETHER WITH YOUR SECRET$cookie_test = md5($array[0] . $secret_code);// IF THE MD5 CODES DO NOT MATCH, THE COOKIE IS NO LONGER INTACTif ($cookie_test == $array[1]){echo PHP_EOL . "THE COOKIE {$_COOKIE["safe_cookie"]} IS INTACT";}else{echo PHP_EOL . "THE COOKIE {$_COOKIE["safe_cookie"]} IS <i>CORRUPT</i>";}
}
else
{die('COOKIE IS SET - REFRESH THE BROWSER WINDOW NOW');
}// SHOW WHAT HAPPENS WITH A CORRUPT COOKIE
$_COOKIE["safe_cookie"] = str_replace('MARY', 'FRED', $_COOKIE["safe_cookie"]);// TEST THE COOKIE AGAIN
if (isset($_COOKIE["safe_cookie"]))
{// BREAK THE COOKIE VALUE APART AT THE DELIMITER$array = explode($dlm, $_COOKIE["safe_cookie"]);// ENCODE THE DATA STRING TOGETHER WITH OUT SECRET$cookie_test = md5($array[0] . $secret_code);// IF THE MD5 CODES DO NOT MATCH, THE COOKIE IS NO LONGER INTACTif ($cookie_test == $array[1]){echo PHP_EOL . "THE COOKIE {$_COOKIE["safe_cookie"]} IS INTACT";}else{echo PHP_EOL . "THE COOKIE {$_COOKIE["safe_cookie"]} IS <i>CORRUPT</i>";}
}

Other Hashing Algorithms

其他散列算法

To move beyond md5() hashing, please have a look at the PHP hash_algos() function to find other hashing algorithms. Here is a little script that you can install and run to see other ways PHP can provide a hash.

要超越md5()哈希,请查看PHP hash_algos()函数以查找其他哈希算法。 这是一个可以安装和运行的小脚本,以查看PHP提供哈希的其他方式。

<?php // demo/hash_algos.php
/*** Demonstrate PHP hashing algorithms** http://php.net/manual/en/book.hash.php* http://php.net/manual/en/function.hash.php*/
error_reporting(E_ALL);
echo '<pre>';// IF THE FORM HAS BEEN SUBMITTED, HASH THE REQUEST VARIABLE
if (!empty($_GET['q']))
{$qget = substr($_GET['q'], 0, 10);$qpad = str_pad($qget, 10, ' ', STR_PAD_RIGHT);$algos = hash_algos();echo PHP_EOL . '                                    1...5...10...15...20...25...30...35...40...45...50...55...60...65...70...75...80...85...90...95..100..105..110..115..120..125..130';echo PHP_EOL . '                                    |        |         |         |         |         |         |         |         |         |         |         |         |         |';foreach ($algos as $algo){$hash = hash($algo, $qget);$algo_p = str_pad($algo . ':' , 12, ' ', STR_PAD_RIGHT);echo PHP_EOL . $qpad . " hashed with $algo_p " . $hash;}
}// PUT UP THE FORM TO RECEIVE THE INPUT STRING
$form = <<<EOF
<form>
String to hash (up to 10 characters): <input name="q" autocomplete="off" />
<input type="submit" />
</form>
EOF;echo $form;

Encryption

加密

Encryption can encode information in ways that make it possible to reverse the process and recover the original clear-text string.  PHP has several built-in encryption functions.  These provide different levels of cryptographic strength.  The rest of this article will show different experiments in encryption and will highlight the strengths and weaknesses of each design.

加密可以以使逆过程和恢复原始明文字符串成为可能的方式对信息进行编码。 PHP具有几个内置的加密功能。 这些提供不同级别的加密强度。 本文的其余部分将展示加密方面的不同实验,并将重点介绍每种设计的优缺点。

With a little forethought we can design the interface for our encryption classes.  There are two important things that we need to do: encrypt and decrypt.

稍加考虑,我们就可以为加密类设计接口 。 我们需要做两个重要的事情:加密和解密。

<?php
/*** The Interface defines the two main data transformation activities*/
Interface Encryption_Interface
{public function encrypt($text, $key);public function decrypt($text, $key);
}

Encryption with MCrypt

使用MCrypt加密

Here is a simple example of reversible encryption using MCrypt.  You can copy this code and install it on your own server to see how it works.  You will see how we implemented the Encryption_Interface.  Note that any encryption algorithm, by itself, may produce binary values that are unsuitable for transport across binary-sensitive boundaries.  For this reason, we use base64_encode() and base64_decode() to produce a binary-safe string.

这是使用MCrypt进行可逆加密的简单示例。 您可以复制此代码并将其安装在自己的服务器上,以查看其工作方式。 您将看到我们如何实现Encryption_Interface。 请注意,任何加密算法本身都可能产生不适合跨二进制敏感边界传输的二进制值。 因此,我们使用base64_encode()和base64_decode()生成二进制安全的字符串。

This is useful as a teaching example, but don't use this in a deployed application.  It has some flaws, most notably, idempotence.  An encryption process that always turns out the same encryption values is subject to rainbow attacks, just like a hashing process that exhibits idempotence.  If we encrypt password with an empty key, we always get 9xp0gHiq+GhGyXQ+1/Y4XuR0RIE/QJ9ZSrZqBLMyzy0=.  If we encrypt password with secret for the key, we always get  PEl/zm3PAwwjrg+Sc8xfELuWin3/Yd8cbtRBnnip/20=.  This kind of predictability is a disadvantage in cryptographic science.

这作为一个教学示例很有用,但不要在已部署的应用程序中使用它。 它有一些缺陷,最明显的是幂等。 始终显示相同加密值的加密过程会遭受彩虹攻击,就像表现出幂等性的哈希过程一样。 如果我们使用空密钥加密

<?php // demo/encrypt_decrypt_mcrypt.php
/*** Show how to encrypt and decrypt information* with binary-safe transport over the internet* Note: ECB may not be the "best" mode, YMMV** http://php.net/manual/en/book.mcrypt.php* http://php.net/manual/en/ref.mcrypt.php* http://php.net/manual/en/mcrypt.ciphers.php** https://en.wikipedia.org/wiki/Advanced_Encryption_Standard* https://en.wikipedia.org/wiki/Base64** Parallel construction in the encrypt() decrypt() methods*/
error_reporting(E_ALL);/*** The Interface defines the two main data transformation activities*/
Interface Encryption_Interface
{public function encrypt($text, $key);public function decrypt($text, $key);
}class Mcrypt Implements Encryption_Interface
{protected $cipher;protected $mode;public function __construct($cipher = MCRYPT_RIJNDAEL_256, $mode = MCRYPT_MODE_ECB){$this->cipher = $cipher;$this->mode   = $mode;}public function encrypt($text, $key){$data = mcrypt_encrypt($this->cipher, $key, $text, $this->mode);return base64_encode($data);}public function decrypt($text, $key){$text = base64_decode($text);$data = mcrypt_decrypt($this->cipher, $key, $text, $this->mode);return $data;}
}// INSTANTIATE AN ENCRYPTION OBJECT FROM THE CLASS
$m = new Mcrypt;// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$encoded = $decoded = NULL;
$secret  = !empty($_POST['secret']) ? $_POST['secret'] : '';// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST["clearstring"]))
{$encoded = $m->encrypt($_POST["clearstring"], $secret);echo "<br/>{$_POST["clearstring"]} YIELDS ENCODED ";var_dump($encoded);
}if (!empty($_POST["cryptstring"]))
{$decoded = $m->decrypt($_POST["cryptstring"], $secret);echo "<br/>{$_POST["cryptstring"]} YIELDS DECODED ";var_dump($decoded);
}// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM
<form method="post">
Secret Key:<input name="secret" value="$secret" autocomplete="off" />
<br>
<textarea name="clearstring">$decoded</textarea>
<input type="submit" value="ENCRYPT" />
<br>
<textarea name="cryptstring">$encoded</textarea>
<input type="submit" value="DECRYPT" />
</form>
FORM;echo $form;

Encryption with MCrypt (Improved)

使用MCrypt加密(已改进)

This example uses MCrypt, and improves on the earlier example by using an initialization vector ("IV").  The IV is a starting variable that is used to provide additional entropy in the output.  A random IV will cause the encrypted values to be different every time, effectively eliminating idempotence and making it more difficult for an attacker to infer relationships between encrypted messages.  Rainbow attacks are much less useful when there is an IV in play.

本示例使用MCrypt,并通过使用初始化向量 (“ IV”)对先前的示例进行了改进。 IV是一个初始变量,用于在输出中提供附加的熵。 随机IV将使加密值每次都不同,从而有效消除了幂等性,并使攻击者更难推断加密消息之间的关系。 进行IV攻击时,Rainbow攻击的作用要小得多。

PHP gives us two functions that make IV creation easy.  mcrypt_get_iv_size() will tell us the byte size of the IV string belonging to our cipher and mode combination.  mcrypt_create_iv() will return an initialization vector (IV) from a random source.  The man page for MCrypt_Create_IV() has some links that provide a "deep dive" into theory and practice of cryptology.  If you're interested in learning more about how this works, it makes for great reading.  Here is the code sample using the IV.

PHP提供了两个使IV创建变得容易的函数。 mcrypt_get_iv_size()将告诉我们属于密码和模式组合的IV字符串的字节大小。 mcrypt_create_iv()将从随机源返回初始化向量(IV)。 MCrypt_Create_IV()的手册页具有一些链接,这些链接提供了对密码学理论和实践的“深入了解”。 如果您有兴趣了解更多有关它的工作原理的信息,那么本书非常适合阅读。 这是使用IV的代码示例。

<?php // demo/encrypt_decrypt_mcrypt_iv.php
/*** Show how to encrypt and decrypt information* with binary-safe transport over the internet* Note: ECB may not be the "best" mode, YMMV** http://php.net/manual/en/book.mcrypt.php* http://php.net/manual/en/ref.mcrypt.php* http://php.net/manual/en/mcrypt.ciphers.php** https://en.wikipedia.org/wiki/Advanced_Encryption_Standard* https://en.wikipedia.org/wiki/Base64** Parallel construction in the encrypt() decrypt() methods*/
error_reporting(E_ALL);/*** The Interface defines the two main data transformation activities*/
Interface Encryption_Interface
{public function encrypt($text, $key);public function decrypt($text, $key);
}class Mcrypt Implements Encryption_Interface
{protected $cipher;protected $mode;protected $ivsize;protected $vector;public function __construct($cipher = MCRYPT_RIJNDAEL_256, $mode = MCRYPT_MODE_ECB){$this->cipher = $cipher;$this->mode   = $mode;$this->ivsize = mcrypt_get_iv_size($cipher, $mode);var_dump($this->ivsize);$this->vector = mcrypt_create_iv($this->ivsize, MCRYPT_RAND);}public function encrypt($text, $key){$data = mcrypt_encrypt($this->cipher, $key, $text, $this->mode, $this->vector);return base64_encode($this->vector . $data);}public function decrypt($text, $key){$text = base64_decode($text);// SEPARATE THE IV AND THE ENCRYPTED DATA$vect = substr($text, 0, $this->ivsize);$text = substr($text, $this->ivsize);$data = mcrypt_decrypt($this->cipher, $key, $text, $this->mode, $this->vector);return $data;}
}// INSTANTIATE AN ENCRYPTION OBJECT FROM THE CLASS
$m = new Mcrypt;// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$encoded = $decoded = NULL;
$secret  = !empty($_POST['secret']) ? $_POST['secret'] : '';// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST["clearstring"]))
{$encoded = $m->encrypt($_POST["clearstring"], $secret);echo "<br/>{$_POST["clearstring"]} YIELDS ENCODED ";var_dump($encoded);
}if (!empty($_POST["cryptstring"]))
{$decoded = $m->decrypt($_POST["cryptstring"], $secret);echo "<br/>{$_POST["cryptstring"]} YIELDS DECODED ";var_dump($decoded);
}// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM
<form method="post">
Secret Key:<input name="secret" value="$secret" autocomplete="off" />
<br>
<textarea name="clearstring">$decoded</textarea>
<input type="submit" value="ENCRYPT" />
<br>
<textarea name="cryptstring">$encoded</textarea>
<input type="submit" value="DECRYPT" />
</form>
FORM;echo $form;

If we encrypt password (even with an empty key), we get a different value every time.  As you can see we have much stronger encryption, and it is much harder to discern patterns with the naked eye or with a database of encrypted strings.  Here are my first few tries.

如果我们加密

1...5...10...15...20...25...30...35...40...45...50...55...60...65...70...75...80...85...
|                              |
| Initialization Vector        | Encrypted Data
|  32 bytes in this example    |  followed by encrypted padding
|                              |
199zZRmtQ2LYxDAc1NOOVwuH/upttDL6wCH7XHsB2HL3GnSAeKr4aEbJdD7X9jhe5HREgT9An1lKtmoEszLPLQ==
ZJ6eXQbXWipyod5vCXGPXcC0gMWzbud6vSeVIKmR0Qb3GnSAeKr4aEbJdD7X9jhe5HREgT9An1lKtmoEszLPLQ==
lSAukpbvItlgzp6TxHSsbZH18EZIRgBdgKYIGI4TjAL3GnSAeKr4aEbJdD7X9jhe5HREgT9An1lKtmoEszLPLQ==

But this is not cryptographically "good enough" any more.  See how the padding characters are replicated?  This means the encryption scheme can be defeated through a Padding Oracle attack.  So it's back to the drawing board, in search of a more secure encryption process.

但是,这在密码上已不再“足够好”。 看看填充字符是如何复制的? 这意味着可以通过Padding Oracle攻击来击败加密方案。 因此,它返回到绘图板上,以寻找更安全的加密过程。

Encryption with OpenSSL

使用OpenSSL加密

The current state of the art in PHP encryption is implemented by the OpenSSL library.  OpenSSL has many features that are not implemented (yet) in PHP, and presently has over 50 functions.  Fortunately we can get good encryption results with only a small subset of the functions!

PHP加密的最新技术是由OpenSSL库实现的。 OpenSSL具有许多PHP尚未实现的功能,目前有50多个功能 。 幸运的是,仅使用一小部分功能,我们就能获得良好的加密效果!

Like our improved MCrypt example, OpenSSL uses an initialization vector to increase the entropy of the encrypted string.  And because this is a more up-to-date example, we use Exception to trap and report any errors.

像我们改进的MCrypt示例一样,OpenSSL使用初始化向量来增加加密字符串的熵。 并且由于这是最新的示例,因此我们使用Exception来捕获和报告任何错误。

<?php // demo/encrypt_decrypt_openssl.php
/*** Show how to encrypt and decrypt information using OpenSSL* with binary-safe (base64) transport over the internet** http://php.net/manual/en/book.openssl.php** https://en.wikipedia.org/wiki/Advanced_Encryption_Standard* https://en.wikipedia.org/wiki/Base64** https://bugs.php.net/bug.php?id=67304** Parallel construction in the encrypt() decrypt() methods*/
error_reporting(E_ALL);/*** The Interface defines the two main data transformation activities*/
Interface Encryption_Interface
{public function encrypt($text, $key);public function decrypt($text, $key);
}class SSLCrypt Implements Encryption_Interface
{protected $cipher_algorithm;protected $digest_method;protected $ivector_length;public function __construct($cipher_algorithm = 'aes-256-ctr', $digest_method = 'sha256'){$this->cipher_algorithm = $cipher_algorithm;$this->digest_method = $digest_method;if (!in_array($cipher_algorithm, openssl_get_cipher_methods(TRUE))) {throw new \Exception(__METHOD__ . " Unknown cipher $cipher_algorithm");}if (!in_array($digest_method, openssl_get_md_methods(TRUE))) {throw new \Exception(__METHOD__ . " Unknown digest $digest_method");}$this->ivector_length = openssl_cipher_iv_length($cipher_algorithm);}public function encrypt($text, $key){$keyhash = openssl_digest($key, $this->digest_method, TRUE);$ivector = mcrypt_create_iv($this->ivector_length, MCRYPT_DEV_URANDOM);$crypted = openssl_encrypt($text, $this->cipher_algorithm, $keyhash, OPENSSL_RAW_DATA, $ivector);if ($crypted === FALSE) {throw new \Exception(__METHOD__ . ' Failed: ' . openssl_error_string());}// RETURN THE IV AND THE ENCRYPTED DATAreturn base64_encode($ivector . $crypted);}public function decrypt($text, $key){$keyhash = openssl_digest($key, $this->digest_method, TRUE);$rawdata = base64_decode($text);if (strlen($rawdata) < $this->ivector_length) {throw new \Exception(__METHOD__ . ' Data is too short');}// SEPARATE THE IV AND THE ENCRYPTED DATA$ivector = substr($rawdata, 0, $this->ivector_length);$rawtext = substr($rawdata, $this->ivector_length);$decrypt = openssl_decrypt($rawtext, $this->cipher_algorithm, $keyhash, OPENSSL_RAW_DATA, $ivector);if ($decrypt === FALSE) {throw new \Exception(__METHOD__ . ' Failed: ' . openssl_error_string());}return $decrypt;}
}// INSTANTIATE ENCRYPTION OBJECTS FROM THE CLASS
$s = new SSLCrypt;// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$s_encoded = $s_decoded = NULL;
$s_secret  = !empty($_POST['s_secret']) ? $_POST['s_secret'] : NULL;// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST))
{if (!empty($_POST["s_clearstring"])){$s_encoded = $s->encrypt($_POST["s_clearstring"], $_POST["s_secret"]);echo "<br/>{$_POST["s_clearstring"]} YIELDS OpenSSL ENCODED ";var_dump($s_encoded);}if (!empty($_POST["s_cryptstring"])){$s_decoded = $s->decrypt($_POST["s_cryptstring"], $_POST["s_secret"]);echo "<br/>{$_POST["s_cryptstring"]} YIELDS OpenSSL DECODED ";var_dump($s_decoded);}
}// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM
<form method="post">
<h3>Using OpenSSL</h3>
<input type="hidden" name="choice" value="ssl" />
Secret Password (Key):
<br>
<input name="s_secret" value="$s_secret" autocomplete="off" />
<br>
<textarea name="s_clearstring">$s_decoded</textarea>
<input type="submit" value="ENCRYPT OpenSSL" />
<br>
<textarea name="s_cryptstring">$s_encoded</textarea>
<input type="submit" value="DECRYPT OpenSSL" />
<br>
</form>
FORM;echo $form;

Our initialization vector is shorter (16 bytes) and so is the encrypted data.  Here are the first few encryption tries using an input string of password with an empty key in OpenSSL.

我们的初始化向量更短(16个字节),加密数据也更短。 这是在OpenSSL中使用

1...5...10...15...20...25...30..
|               |
| Initial'n     | Encrypted
|  Vector       |  Data
|  16 bytes     |
|               |
0RmMD1ev5MVBXxKj2Dcfs8ciSXgDPgyx
j+odB29EPsb3i9TasNHXL28jiHN5zBSq
+2aiANr9CA5TJL2Y82zmIK/i0N+cFV3h

Encryption with OpenSSL and Message Authentication

使用OpenSSL和消息身份验证进行加密

As good as the OpenSSL example might be, it can be improved.  The next code snippet shows a multi-faceted approach to encryption and authentication.  In this design we transmit three data values: The IV, the encrypted data, and the Message Authentication Code ("MAC").  Because the base64() alphabet does not contain the pipe character, we can use the pipe as a delimiter to separate components of the message.  Here is the code that generates an encrypted message under the Encrypt-then-Authenticate strategy.  This strategy means that the first operation on the receiving end can be authentication, and any message that fails authentication can be discarded - it does not have to be decrypted at all.

尽管可以使用OpenSSL示例,但可以对其进行改进。 下一个代码片段展示了一种用于加密和身份验证的多方面方法。 在此设计中,我们传输三个数据值:IV,加密的数据和消息身份验证代码 (“ MAC”)。 因为base64()字母不包含竖线字符,所以我们可以使用竖线作为分隔符来分隔消息的各个组成部分。 这是在“加密然后验证”策略下生成加密消息的代码。 这种策略意味着接收端的第一个操作可以是身份验证,而任何未通过身份验证的消息都可以被丢弃-完全不必对其进行解密。

<?php // demo/encrypt_decrypt_openssl_auth.php
/*** Show how to encrypt and decrypt information* with Encrypt-then-Authenticate design* with binary-safe (base64) transport over the internet** http://php.net/manual/en/book.openssl.php** https://en.wikipedia.org/wiki/Advanced_Encryption_Standard* https://en.wikipedia.org/wiki/Base64* https://en.wikipedia.org/wiki/Authenticated_encryption** https://bugs.php.net/bug.php?id=67304** Parallel construction in the encrypt() decrypt() methods*/
error_reporting(E_ALL);/*** The Interface defines the two main data transformation activities*/
Interface Encryption_Interface
{public function encrypt($text, $key);public function decrypt($text, $key);
}class SSLCrypt Implements Encryption_Interface
{protected $cipher_algorithm;protected $digest_method;protected $vector_length;public function __construct($cipher_algorithm = 'aes-256-ctr', $digest_method = 'sha256'){$this->cipher_algorithm = $cipher_algorithm;$this->digest_method = $digest_method;if (!in_array($cipher_algorithm, openssl_get_cipher_methods(TRUE))) {throw new \Exception(__METHOD__ . " Unknown cipher $cipher_algorithm");}if (!in_array($digest_method, openssl_get_md_methods(TRUE))) {throw new \Exception(__METHOD__ . " Unknown digest $digest_method");}$this->ivector_length = openssl_cipher_iv_length($cipher_algorithm);}public function encrypt($text, $key){$keyhash = openssl_digest($key, $this->digest_method, TRUE);$ivector = mcrypt_create_iv($this->ivector_length, MCRYPT_DEV_URANDOM);$crypted = openssl_encrypt($text, $this->cipher_algorithm, $keyhash, OPENSSL_RAW_DATA, $ivector);if ($crypted === FALSE) {throw new \Exception(__METHOD__ . ' Failed: ' . openssl_error_string());}// RETURN THE AUTH DIGEST, IV, AND THE ENCRYPTED DATA$payload = base64_encode($ivector . $crypted);var_dump($payload);$digest  = openssl_digest($key . $payload, $this->digest_method);var_dump($digest);return $digest . '|' . $payload;}public function decrypt($text, $key){$keyhash = openssl_digest($key, $this->digest_method, TRUE);// SEPARATE AND TEST THE AUTH DIGEST$inputs  = explode('|', $text);$digest  = $inputs[0];$compare = openssl_digest($key . $inputs[1], $this->digest_method);if ($compare != $digest){throw new \Exception(__METHOD__ . ' Authentication digest failure');}$rawdata = base64_decode($inputs[1]);// SEPARATE THE IV AND THE ENCRYPTED DATA$ivector = substr($rawdata, 0, $this->ivector_length);$rawtext = substr($rawdata, $this->ivector_length);$decrypt = openssl_decrypt($rawtext, $this->cipher_algorithm, $keyhash, OPENSSL_RAW_DATA, $ivector);if ($decrypt === FALSE) {throw new \Exception(__METHOD__ . ' Failed: ' . openssl_error_string());}return $decrypt;}
}// INSTANTIATE ENCRYPTION OBJECTS FROM THE CLASS
$s = new SSLCrypt;// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$s_encoded = $s_decoded = NULL;
$s_secret  = !empty($_POST['s_secret']) ? $_POST['s_secret'] : NULL;// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST))
{if (!empty($_POST["s_clearstring"])){$s_encoded = $s->encrypt($_POST["s_clearstring"], $_POST["s_secret"]);echo "<br/>{$_POST["s_clearstring"]} YIELDS OpenSSL ENCODED ";var_dump($s_encoded);}if (!empty($_POST["s_cryptstring"])){$s_decoded = $s->decrypt($_POST["s_cryptstring"], $_POST["s_secret"]);echo "<br/>{$_POST["s_cryptstring"]} YIELDS OpenSSL DECODED ";var_dump($s_decoded);}
}
// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM<form method="post">
<h3>Using OpenSSL and MAC</h3>
Secret Password (Key):
<br>
<input name="s_secret" value="$s_secret" autocomplete="off" />
<br>
<textarea name="s_clearstring">$s_decoded</textarea>
<input type="submit" value="ENCRYPT OpenSSL" />
<br>
<textarea name="s_cryptstring">$s_encoded</textarea>
<input type="submit" value="DECRYPT OpenSSL" />
<br>
</form>FORM;echo $form;

Here are the first few tries, showing the MAC, followed by the IV and encrypted message.

这是前几次尝试,显示了MAC,然后是IV和加密的消息。

1...5...10...15...20...25...30...35...40...45...50...55...60...65...70...75...80...85...90...95..
|                                                                |
| Message Authentication Code                                    | Payload
|  Digest of Key and Data                                        |              |
|                                                                | Initial'n    | Encrypted
|                                                                |  Vector      |  Data
|                                                                |              |
16565deb7c4b4c6c13aa42c9653c434b0c5a6247977d5d71dc40b890f125e7c3|egl00WlazSOcDsxDV5iBhMnXpjY74aCF
cddf5078c7ddef45666b45e0a7ee0f16e936ebf8a43cc2d327e99e26740cb0ae|uQOtWB7hhq8Ob1WEbVgEaJAr23aU52bN
aca329b53db224c44d640b75ecfd237c00e6c827945273b5f6ea22f6e14c1d5c|DRFAtnQqIxnnYyMvI4kd5Qb0yvu1TXSO|| Pipe Delimiter

Encryption with OpenSSL, Message Authentication, and Origin Verification

使用OpenSSL加密,消息身份验证和原始验证

Any encryption scheme helps secure our messages and keep them away from prying eyes.  And (of course) we only make our RESTful API communications over HTTPS.  But what if these techniques break down, and we receive a message that is encoded with our expected algorithm, uses our secret key, but comes from an attacker?  What are the chances of this happening?  Very small, you would say, and I would agree.  But there is a slightly greater-than-zero possibility, however small.  Disgruntled ex-employees may have access to the secrets of our communication strategy.  The only way to protect against such an attack is with origin verification.

任何加密方案都可以帮助保护我们的邮件,并使它们不被窥视。 并且(当然),我们仅通过HTTPS进行RESTful API通信。 但是,如果这些技术崩溃了,并且我们收到了用我们期望的算法编码,使用我们的密钥但来自攻击者的消息,该怎么办? 发生这种情况的机会是什么? 您会说很小,我会同意。 但是存在很小的大于零的可能性,但是很小。 心怀不满的前雇员可能会接触到我们沟通策略的秘密。 防止此类攻击的唯一方法是使用来源验证。

In the origin verification design, we make use of the MAC.  On the originating server, we make a database record of each encrypted message by storing the MAC in a database table.  On the receiving server, we extract the MAC from the message and make a POST-method request back to the originating server, sending the MAC.  The originating server uses the MAC that we posted to make a database lookup, and if the MAC is found, it deletes the MAC from its table and sends a 200 OK response with the word, "Verified" in the body of the response.  Upon receiving the response with "Verified" the receiving server knows that the message origin has been verified.  Now the receiver can proceed with MAC verification and decryption, secure in the knowledge that the message came from a trusted source.  This simple and elegant "handshake" has been used for PayPal communications since the 1990's and it still works well today.

在来源验证设计中,我们利用了MAC。 在原始服务器上,我们通过将MAC存储在数据库表中来对每个加密消息进行数据库记录。 在接收服务器上,我们从消息中提取MAC,然后向原始服务器发出POST方法请求,发送MAC。 原始服务器使用我们发布的MAC进行数据库查找,如果找到该MAC,它将从其表中删除该MAC,并在响应的正文中发送200 OK响应,并带有单词“ Verified”。 在接收到带有“已验证”的响应时,接收服务器知道消息来源已被验证。 现在,接收器可以继续进行MAC验证和解密,从而确保消息来自受信任的源。 从1990年代开始,这种简单而优雅的“握手”就已经用于PayPal通信,并且在今天仍然有效。

Summary

摘要

This article has shown us some of the history of information hiding technologies, and some of the PHP best practices for secure communications today.  The article was written in 2016, and technology is always advancing.  So it might be good to check out the references if you're thinking of using these code snippets in a deployed application.

本文向我们展示了一些信息隐藏技术的历史,以及当今用于安全通信PHP最佳实践。 这篇文章写于2016年,技术一直在进步。 因此,如果您正在考虑在已部署的应用程序中使用这些代码段,则最好检查一下引用。

References and Further Reading

参考资料和进一步阅读

https://www.owasp.org/index.php/Main_Pagehttps://www.owasp.org/index.php/Main_Page https://en.wikipedia.org/wiki/Advanced_Encryption_Standardhttps://en.wikipedia.org/wiki/Advanced_Encryption_Standard https://en.wikipedia.org/wiki/Authenticated_encryptionhttps://en.wikipedia.org/wiki/Authenticated_encryption https://en.wikipedia.org/wiki/Galois/Counter_Modehttps://en.wikipedia.org/wiki/Galois/Counter_Mode https://en.wikipedia.org/wiki/Base64https://zh.wikipedia.org/wiki/Base64 https://secure.php.net/manual/en/book.openssl.phphttps://secure.php.net/manual/zh/book.openssl.php https://bugs.php.net/bug.php?id=67304https://bugs.php.net/bug.php?id=67304 https://moxie.org/blog/the-cryptographic-doom-principle/https://moxie.org/blog/the-cryptographic-doom-principle/

Please give us your feedback!

请给我们您的反馈意见!

If you found this article helpful, please click the "thumb's up" button below. Doing so lets the E-E community know what is valuable for E-E members and helps provide direction for future articles.  If you have questions or comments, please add them.  Thanks!

如果您发现本文有帮助,请单击下面的“竖起大拇指”按钮。 这样做可以使EE社区了解对EE成员有价值的内容,并为将来的文章提供指导。 如果您有任何问题或意见,请添加。 谢谢!

翻译自: https://www.experts-exchange.com/articles/28835/Keeping-Secrets-with-PHP.html

用php写后端怎么使用模板

用php写后端怎么使用模板_用PHP保守秘密相关推荐

  1. 后端思维篇:手把手教你写一个并行调用模板

    前言 36个设计接口的锦囊中,也提到一个知识点:就是使用并行调用优化接口.所以接下来呢,就快马加鞭写第二篇:手把手教你写一个并行调用模板~ 一个串行调用的例子(App首页信息查询) Completio ...

  2. Java模板引擎解析原理_关于前后端分离与模板引擎

    随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,往往需要针对不同的终端开发定制的版本,为了提升开发效率,前后端分离的需要越来 ...

  3. Vue2.0 + ElementUI 手写权限管理系统后台模板(一)——简述

    简介 这个权限管理就是为了方便,跟系统安全真的不沾边,只是根据后台返回的角色信息来生成他可以看见的菜单和按钮,显示菜单的方法是根据权限删除掉路由表里没有权限的路由,然后再动态添加,原本包含没有访问权限 ...

  4. 计算机调查应用表格,大学计算机实验课_调查报告_表格模板_应用文书.doc

    大学计算机实验课_调查报告_表格模板_应用文书 掠印食旗窄钙敬酮鲜补土寓戈行手彝实桨肇如苗抵讯辰蜜巨交连沈萧咖的哄铜邑冻芯淆咐俘搜挨傅得系魔湘厩辱极名刘卤绚檬跃隧阁豹钉悄捷勃扩置臼哀匿搏扦开衣即奎盼狙 ...

  5. Html5浪漫结婚请柬婚礼网站模板❤_爱她就给她最美的H5婚礼请柬_(婚庆电子邀请函)含背景音乐...

    ❉ Html5浪漫结婚请柬婚礼网站模板❤_爱她就给她最美的H5婚礼请柬_(婚庆电子邀请函)含背景音乐 一年一度的/520情人节/七夕情人节/生日礼物/告白师妹/程序员表白, 这个是一个简单得html得 ...

  6. Html5浪漫结婚请柬婚礼网站模板❤_爱她就给她最美的H5婚礼请柬_(婚庆电子邀请函)含背景音乐

    ❉ Html5浪漫结婚请柬婚礼网站模板❤_爱她就给她最美的H5婚礼请柬_(婚庆电子邀请函)含背景音乐 一年一度的/520情人节/七夕情人节/生日礼物/告白师妹/程序员表白, 这个是一个简单得html得 ...

  7. 第十二届_国赛蓝桥杯个人模板_基础篇

    第十二届_国赛蓝桥杯个人模板_网格图_DFS/BFS篇 第十二届_国赛蓝桥杯个人模板_全排列_DFS/BFS篇 第十二届_国赛蓝桥杯个人模板_DP篇 第十二届_国赛蓝桥杯个人模板_数论篇 第十二届_国 ...

  8. 留学计算机Ps模板,留学ps怎么写?出国留学ps模板

    留学ps怎么写?出国留学ps模板After passing out of engineering college in 1999 with distinction and honors in my B ...

  9. 前端老弟第一次写后端,崩了!

    幽默轻松小知识,一起来看看老弟第一次写的后端代码,你觉得如何? 大家好,我是鱼皮,今天分享我的老弟第一次写后端代码时出现的囧事,希望大家引以为戒. 孽起 我的老弟小阿巴,目前大一,自学编程有一段时间了 ...

最新文章

  1. 怎么让上下两排对齐_为什么你家装饰画怎么挂都怪怪的?看完再装立马就能美翻了!...
  2. 第6章-一阶多智体系统一致性-->6.2 离散时间多智能体系统一致性
  3. PHP-Codeigniter:实习笔记1
  4. nodejs+express整合kindEditor实现图片上传 - 木子丰咪咕晶 - 开源中国
  5. jsp实现数据禁用和只读
  6. 按周汇总_有合并单格及空行的数据如何快速汇总?简单几步快速搞定
  7. python连接sql sever_R和python连接SQL sever 数据库操作
  8. matlab %%?
  9. java字符串反转异或_字符串反转总结】Java中七种方法实现
  10. ubuntu wiznote 无法显示内容 只有标题
  11. oracle18c 配置,0报错!Oracle 18C 完全安装指南及常见问题汇总
  12. 零点定理和罗尔定理的完善?
  13. 【Matlab学习手记】bsxfun的使用
  14. 说说org.json.JSONObject功能和源码(二)
  15. 机器人“嘚瑟”怎么办?用加速度指令“hold”住它!
  16. 使用计算机生成景物图像,使用计算机生成假想景物的图像,其主要步骤是______。...
  17. ant-design-vue 中标签页tab上额外的元素(tabBarExtraContent) , tab选项卡头增加文字
  18. 6个P2P流媒体开源项目介绍
  19. 2022-2023 计算机视觉顶会截止时间
  20. 车联网终端4G-TBOX新能源汽车/汽油车/货车定位终端

热门文章

  1. Camtasia v2021.18汉化屏幕录像软件教程分享
  2. 当ChatGpt接入微信群之后。。。
  3. 使用 Python 和 Bitly 缩短您的 URL
  4. 电源适配器的主要质量指标
  5. OpenCV-Python教程:绘制直线、圆形、方形(line,circle,rectangle)
  6. 基于java+jsp+mysql教师教学质量测评系统
  7. FastTunnel - 免费好用的内网穿透工具搭建教程
  8. 【matlab】输入一字符串,字母大写变小写,小写变大写。
  9. 天猫2月咖啡行业数据分析(咖啡品牌销量排行)
  10. 地铁译:Spark for python developers ---Spark的数据戏法