我正在使用php创建一个Web应用程序,这个Web应用程序在我的家中完美运行但是当我尝试在一个公司中使用其中两个网络不起作用时,我的意思是我可以访问该网站但有很多会话丢失,即使我不刷新或导航也会被破坏,特别是当我每个人都在使用互联网时,有时当我进入页面时,这会将我重定向到一个类似于开头的域:ww7
这是我的代码:
<?php
//Genera un password para el empleado o el cliente
function generaPass(){
//Se define una cadena de caractares. Te recomiendo que uses esta.
$cadena = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
//Obtenemos la longitud de la cadena de caracteres
$longitudCadena=strlen($cadena);
//Se define la variable que va a contener la contraseña
$pass = "";
//Se define la longitud de la contraseña, en mi caso 10, pero puedes poner la longitud que quieras
$longitudPass=10;
//Creamos la contraseña
for($i=1 ; $i<=$longitudPass ; $i++){
//Definimos numero aleatorio entre 0 y la longitud de la cadena de caracteres-1
$pos=rand(0,$longitudCadena-1);
//Vamos formando la contraseña en cada iteraccion del bucle, añadiendo a la cadena $pass la letra correspondiente a la posicion $pos en la cadena de caracteres definida.
$pass .= substr($cadena,$pos,1);
}
return $pass;
}
/**
* @param $required_fields_array, n array containing the list of all required fields
* @return array, containing all errors
*/
function check_empty_fields($required_fields_array){
//initialize an array to store error messages
$form_errors = array();
//loop through the required fields array snd popular the form error array
foreach($required_fields_array as $name_of_field){
if(!isset($_POST[$name_of_field]) || $_POST[$name_of_field] == NULL){
$form_errors[] = $name_of_field . " is a required field";
}
}
return $form_errors;
}
/**
* @param $fields_to_check_length, an array containing the name of fields
* for which we want to check min required length e.g array('username' => 4, 'email' => 12)
* @return array, containing all errors
*/
function check_min_length($fields_to_check_length){
//initialize an array to store error messages
$form_errors = array();
foreach($fields_to_check_length as $name_of_field => $minimum_length_required){
if(strlen(trim($_POST[$name_of_field])) < $minimum_length_required && $_POST[$name_of_field] != NULL){
$form_errors[] = $name_of_field . " is too short, must be {$minimum_length_required} characters long";
}
}
return $form_errors;
}
/**
* @param $data, store a key/value pair array where key is the name of the form control
* in this case 'email' and value is the input entered by the user
* @return array, containing email error
*/
function check_email($data){
//initialize an array to store error messages
$form_errors = array();
$key = 'email';
//check if the key email exist in data array
if(array_key_exists($key, $data)){
//check if the email field has a value
if($_POST[$key] != null){
// Remove all illegal characters from email
filter_var($_POST[$key], FILTER_SANITIZE_EMAIL);
//check if input is a valid email address
if(filter_var($_POST[$key], FILTER_VALIDATE_EMAIL) === false){
$form_errors[] = $key . " is not a valid email address";
}
}
}
return $form_errors;
}
/**
* @param $form_errors_array, the array holding all
* errors which we want to loop through
* @return string, list containing all error messages
*/
function show_errors($form_errors_array){
$errors = "<p><ul style='color: red;'>";
//loop through error array and display all items in a list
foreach($form_errors_array as $the_error){
$errors .= "<li> {$the_error} </li>";
}
$errors .= "</ul></p>";
return $errors;
}
/**
* @param $message, message to display
* @param string $passOrFail, test condition to determine message type
* @return string, returns the message
*/
function flashMessage($message, $passOrFail = "Fail"){
if($passOrFail === "Pass"){
$data = "<div class='alert alert-success'>{$message}</p>";
}else{
$data = "<div class='alert alert-danger'>{$message}</p>";
}
return $data;
}
/**
* @param $page, redirect user to page specified
*/
function redirectTo($page){
header("Location: {$page}.php");
}
/**
* @param $table, table that we want to search
* @param $column_name, the column name
* @param $value, the data collected from the form
* @param $db, database object
* @return bool, returns true if record exist else false
*/
function checkDuplicateEntries($table, $column_name, $value, $db){
try{
$sqlQuery = "SELECT * FROM $table WHERE $column_name=:$column_name";
$statement = $db->prepare($sqlQuery);
$statement->execute(array(":$column_name" => $value));
if($row = $statement->fetch()){
return true;
}
return false;
}catch (PDOException $ex){
//handle exception
}
}
function signout(){
unset($_SESSION['username']);
unset($_SESSION['id']);
unset($_SESSION['coa']);
unset($_SESSION['registrar_usuarios']);
unset($_SESSION['capturar_pedidos']);
unset($_SESSION['salida_materiales']);
unset($_SESSION['alta_clientes']);
unset($_SESSION['alta_productos']);
unset($_SESSION['usuario_cliente']);
unset($_SESSION['cliente']);
session_destroy();
session_regenerate_id(true);
redirectTo('index');
}
/**
*
* @return bool, true if all good
*/
function guard(){
$isValid = true;
$fingerprint = md5($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']);
if((isset($_SESSION['fingerprint']) && $_SESSION['fingerprint'] != $fingerprint)){
$isValid = false;
signout();
}
return $isValid;
}
function isValidImage($file){
$form_errors = array();
//split file name into an array using the dot (.)
$part = explode(".", $file);
//target the last element in the array
$extension = end($part);
switch(strtolower($extension)){
case 'jpg':
case 'gif':
case 'bmp':
case 'png':
return $form_errors;
}
$form_errors[] = $extension . " is not a valid image extension";
return $form_errors;
}
function uploadAvatar($username){
if($_FILES['avatar']['tmp_name']){
//File in the temp location
$temp_file = $_FILES['avatar']['tmp_name'];
$ext = pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION);
$filename = $username.md5(microtime()).".{$ext}";
$path = __DIR__ . "/../../../../uploadscsrnacional/{$filename}"; //uploads/demo.jpg
move_uploaded_file($temp_file, $path);
return $path;
}
return false;
}
function _token(){
$randonToken = base64_encode(openssl_random_pseudo_bytes(32));
//$randonToken = md5(uniqid(rand(), true))." md5";
return $_SESSION['token'] = $randonToken;
}
function validate_token($requestToken){
if(isset($_SESSION['token']) && $requestToken === $_SESSION['token']){
unset($_SESSION['token']);
return true;
}
return false;
}
function prepLogin ($id, $username, $coa,$registrar_usuarios, $capturar_pedidos,$salida_materiales,$alta_clientes,$alta_productos, $usuario_cliente, $cliente){
$_SESSION['id'] = $id;
$_SESSION['username'] = $username;
$_SESSION['coa'] = $coa;
$_SESSION['registrar_usuarios'] = $registrar_usuarios;
$_SESSION['capturar_pedidos'] = $capturar_pedidos;
$_SESSION['salida_materiales'] = $salida_materiales;
$_SESSION['alta_clientes'] = $alta_clientes;
$_SESSION['alta_productos'] = $alta_productos;
$_SESSION['usuario_cliente'] = $usuario_cliente;
$_SESSION['cliente'] = $cliente;
$fingerprint = md5($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']);
$_SESSION['fingerprint'] = $fingerprint;
echo $welcome = "<script type=\"text/javascript\">
swal({
title: \"Welcome back $username! \",
text: \"You're being logged in.\",
type: 'success',
timer: 3000,
showConfirmButton: false });
setTimeout(function(){
window.location.href = 'index.php';
}, 3000);
</script>";
}
这是子文件夹上方的.htaccess,因为我的整个项目都在这个子文件夹中,所以我的项目在另一个旧项目中,我不知道是谁创建了这个网站
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
答案 0 :(得分:1)
我在这里看到的主要问题是,除非我遗漏了某些内容,否则可能会为您的域创建一个cookie,或者为您的域修改cookie并以其他用户身份登录。
我接受了这个功能并删除了你的所有评论,并添加了我自己的一些评论。首先,我们将努力解决您的问题,这不起作用。然后我们应该解决用于此的方法的脆弱性。
<?php
//add these at the top of you script - to make sure we can see the errors
ini_set('display_errors', 1 );
error_reporting(-1);
function isCookieValid(PDO $db){
//Debug the $db variable - is this a valid PDO connetion. Added type hinting.
echo "<pre>"; //for debugging formatting
$isValid = false;
if (isset($_COOKIE['rememberUserCookie'])) {
// This is vaunerable to spoofing
$decryptCookieData = base64_decode($_COOKIE['rememberUserCookie']);
//check the cookie value
var_dum($decryptCookieData);
$user_id = explode("UaQteh5i4y3dntstemYODEC", $decryptCookieData);
$userID = $user_id[1];
//check the userID after parsing it from the cookie
var_dum($userID);
//$sqlQuery = "SELECT * FROM users WHERE id = :id";
$sqlQuery = "SELECT username FROM users WHERE id = :id"; //technically we only need the username
$statement = $db->prepare($sqlQuery);
$statement->execute(array(':id' => $userID));
//Varify that the user exists in the DB
if(false !== ( $username = $statement->fetchColumn(0))){ //fetch column instead
/*
- A lot of unnessacry assginements here.
$id = $row['id'];
$username = $row['username'];
$_SESSION['id'] = $id;
$_SESSION['username'] = $username;
*/
$_SESSION['id'] = $userID; //we already know this value, if we didn't we wouldnt be here
$_SESSION['username'] = $username;
$isValid = true;
}else{
//Debug for cookie failures
var_dump("Cookie user {$userID} not found" );
$isValid = false;
signout(); //<--- signout is called here, obviously
/*
so if this IF condition fails for some reason, the
cookie is deleted, this could include DB errors
etc. depending on your error reporting level, this
may not be obvious
*/
}
}else{
var_dump("No Cookie found" );
}
return $isValid;
}
希望其中一些调试输出能够揭示正在发生的事情。
现在这是多么脆弱。此代码使用不加密的base64
,其编码并没有提供任何安全性优势。因此,让我们分析易受攻击者的意思,专注于此代码:
$decryptCookieData = base64_decode($_COOKIE['rememberUserCookie']);
$user_id = explode("UaQteh5i4y3dntstemYODEC", $decryptCookieData);
$userID = $user_id[1];
$sqlQuery = "SELECT username FROM users WHERE id = :id";
$statement = $db->prepare($sqlQuery);
$statement->execute(array(':id' => $userID));
if(false !== ( $username = $statement->fetchColumn(0))){
$_SESSION['id'] = $userID;
$_SESSION['username'] = $username;
$isValid = true;
}else{
...
}
所以我们在这里做的是:
现在考虑我是否在您的系统上说过一个基本帐户,也许您有免费会员资格和付费会员资格。我可以把我的&#34;记住我&#34;来自该免费帐户的cookie,并编辑其值。这会让我把任何userID放在我想要的地方。这就像将cookie改为这样的值一样简单:
VWFRdGVoNWk0eTNkbnRzdGVtWU9ERUM2NjY=
当此脚本处理时,会尝试以用户666
身份登录。我可以尝试尽可能多的整数ID值,直到我得到一个工作。
现在可以争辩说,我有一个优势,因为我可以看到源代码。但是,查看cookie并查看结尾=
只需要很少的想象力。对我来说,这是一个非常清楚的迹象,它的base64编码。以这种方式编码cookie数据并不罕见。然后我会简单地解码它。然后我会看到这样的事情:
`UaQteh5i4y3dntstemYODEC{myID}`
通常,很少或根本不需要从帐户持有人那里隐藏帐户ID。因此,在此结束时看到这个数字以及它永远不会改变的事实会让我知道它是什么。我已经看到人们用更少的信息找出更难的东西,所以它完全有可能,而且有可能有人想出来。
现在,幸运的是,我们可以采取一些简单的方法来解决这个问题。
最明显的是对cookie值使用真正的加密。这可能是AES256,甚至可能是OpenSSL。这没关系,也许是最简单的事情。但是,如果我们能够避免,我们真的不想向客户提供任何重要数据。当然,我们不想在客户端系统上存储帐户信息(用户ID是帐户信息)。
更好的选择是打破用户帐户和cookie数据之间的联系。这可以通过使用表来存储此功能的数据来完成。然后,当设置记住我时,我们在该表中为用户创建新记录。这会抽象出用户ID,并要求在访问其帐户之前设置该记录。
当我构建这些内容时,我通常将它们与一个忘记密码&#34;特征。我喜欢它所有的#34;护照&#34;双关语。与记住我的唯一区别是,它是使用电子邮件中发送的链接而不是cookie完成的。哦,通常密码重置是一次。但是,您可以在同一个表中为两者设置记录,并在电子邮件中发送查找哈希以进行重置,并将其保存在cookie中以便记住我。
这要好得多,但通过默默无闻的安全并不是真正的安全。所以我们仍然可以做得更好。
我们可以在此记录上设置一个时间限制,比如1个月的好处,当他们访问该网站时刷新。如果在到期后使用它,我们强制登录。
我们可以跟踪用户的IP,只有当创建的IP与当前的IP匹配时才使用该记录(我们希望通知用户自动登录只能在同一个IP上工作,所以他们这样做不要以为它坏了)
我们可以随时删除此记录,我们无法删除用户ID,这就是我们需要将这些数据分开的原因。即。我们可以轻松禁用此功能。
我们可以向用户发送电子邮件确认电子邮件,说明他们是使用自动功能登录的。这会通知他们有人在他们不知情的情况下访问了他们的帐户。
我们可以存储创建cookie时使用的用户代理,这不应该真正改变。如果他们在Edge中创建了cookie,他们就没有在Firefox中使用它的业务。例如。
我们可以提出质询问题,但我真的很讨厌这些问题,而且有点挫败了目的。
我们可以而且应该跟踪帐户登录的方式和时间。这在某种程度上取决于您的申请。
我相信我们还可以采取其他措施来绊倒黑客。任何限制使用它的东西,虽然仍然对用户透明,但安全性不应仅仅基于一件事。
正如我所说,我可以帮助你重构这一点,这样它就是安全的。首先,我们应该解决cookie问题。一旦解决了这个问题,你就不能忽视我所指出的安全隐患。这只是一个很大的漏洞,无法插入。
欢呼声。
<强>更新强> 我在评论中提到过它,所以我想我会举例说明如何使用MySql获取数据库模式来验证列名和表名等。(我从一些代码改编了这个,所以我做了#39;真的测试它,但它应该是接近的)
$statement = 'SELECT `TABLE_NAME`, `ENGINE`, `TABLE_SCHEMA` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` LIKE :database';
$stmt = $DB->prepare($statement);
$stmt->execute([':database' => $database]);
$shcema = [];
while($row = $stmt->fetch(\PDO::FETCH_ASSOC)){
$shcema[$row['TABLE_NAME']] = ['_TABLE_NAME_'=>$row['TABLE_NAME'], '_ENGINE_'=>$row['ENGINE'],'_COLUMNS_' => []];
$statement = 'SHOW COLUMNS FROM `'.$row['TABLE_SCHEMA'].'`.`'.$row['TABLE_NAME'].'`';
$s = $DB->query($statement);
while($r = $s->fetch(\PDO::FETCH_ASSOC)){
$shcema[$row['TABLE_NAME']]['_COLUMNS_'][$r['Field']] = $r;
}
}
这应该产生类似这样的东西
$shcema = [ 'someTable' => [
'_TABLE_NAME_' => 'someTable',
'_ENGINE_' => 'InnoDB',
'_COLUMNS_' => [
'id' =>[
'Field' => 'id',
'Type' => 'int(10) unsigned',
'Null' => 'NO',
'Key' => 'PRI',
'Default' => NULL,
'Extra' => 'auto_increment',
],[
'create_date' =>[
'Field' => 'create_date',
'Type' => 'date',
'Null' => 'YES',
'Key' => '',
'Default' => NULL,
'Extra' => '',
]
]
]
],[ 'someother_table' => [..]
];
这可能超出了你的需要,但基本上你只需给它一个数据库名称,它就会找到所有的表并为它们的模式构建一个数组。它非常酷,因为你可以做一些整洁的东西。