# Авторизация через .htaccess
У сервера `apache` есть возможность сделать базовую авторизацию. Чтобы закрыть директорию, в неё нужно поместить два файла – `.htaccess` и `.htpasswd`.
## Содержание файла .htaccess
```bash
# .htaccess
AuthType Basic
AuthName "Authorization"
AuthUserFile /путь_до_директории/.htpasswd
Require valid-user
```
`AuthName "Authorization"` – сообщение в окне ввода логина и пароля, кириллица не поддерживается, в Google Chrome вообще не выводится.
`AuthUserFile /путь_до_директории/.htpasswd` – путь до файла с паролями.
Авторизацией можно закрыть только определенные файлы, например `ZIP-архивы`.
```bash
# .htaccess
<Files arhive.zip>
AuthType Basic
AuthName "Authorization"
AuthUserFile /путь_до_директории/.htpasswd
Require valid-user
</Files>
```
На некоторых хостингах авторизация на статические файлы (изображения, шрифты и т.д.) может не работать т.к. они отдаются через `Nginx`.
Стоит проверить прямой доступ к самим файлам `.htaccess` и `.htpasswd` из браузера, если да, то закрыть его:
```bash
# .htaccess
<FilesMatch ".(htaccess|htpasswd)$">
Order Allow,Deny
Deny from all
</FilesMatch>
```
## Пароли в htpasswd
В файле хранится пары логина и хеша пароля, например:
```
admin:$apr1$TCrF2kqA$TSMYziwt.qCkrct9yx4vv1
```
Логин может содержать латинские буквы, цифры, - и _, регистрозависимый.
### Создание пароля в MD5
[Подробности](https://runebook.dev/ru/docs/apache_http_server/programs/htpasswd)
```
htpasswd -m .htpasswd user
```
### Bcrypt
В настоящее время считается очень безопасным, работает начиная с версии 2.4, формат:
`$2y$` или `$2a$` + результат алгоритма `crypt_blowfish`.
```php
<?php
function bcrypt($password)
{
$rounds = 12;
$salt = sprintf('$2a$%02d$', $rounds) . substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22);
return crypt($password, $salt);
}
echo bcrypt('123456'); // $2a$12$dMHIiiPfeSMxqj3/Wt1.z.Mo7NPza1x/WANl7hDXZJzxxKKorz5um
```
### MD5 (APR)
Специфический алгоритм `Apache` (1000 итераций `MD5` случайной соли и пароля), работает во всех версиях.
`$apr1$` + результат алгоритма.
```php
<?php
function crypt_apr1_md5($password)
{
$salt = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 8);
$len = strlen($password);
$text = $password . '$apr1$' . $salt;
$bin = pack('H32', md5($password . $salt . $password));
for($i = $len; $i > 0; $i -= 16) {
$text .= substr($bin, 0, min(16, $i));
}
for($i = $len; $i > 0; $i >>= 1) {
$text .= ($i & 1) ? chr(0) : $password{0};
}
$bin = pack('H32', md5($text));
for($i = 0; $i < 1000; $i++) {
$new = ($i & 1) ? $password : $bin;
if ($i % 3) {
$new .= $salt;
}
if ($i % 7) {
$new .= $password;
}
$new .= ($i & 1) ? $bin : $password;
$bin = pack('H32', md5($new));
}
$tmp = '';
for ($i = 0; $i < 5; $i++) {
$k = $i + 6;
$j = $i + 12;
if ($j == 16) $j = 5;
$tmp = $bin[$i] . $bin[$k] . $bin[$j] . $tmp;
}
$tmp = chr(0) . chr(0) . $bin[11] . $tmp;
$tmp = strtr(
strrev(substr(base64_encode($tmp), 2)),
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
'./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
);
return '$apr1$' . $salt . '$' . $tmp;
}
echo crypt_apr1_md5('123456'); // $apr1$h9j4azoy$unmKNqjZlRfZv5xRetm9p1
```
### Crypt
Работает только на `Unix` хостингах и до версии `Apache 2.2.17`. Ограничивает длину пароля до 8 символов. Считается небезопасным.
```php
<?php
function crypt3($password)
{
return crypt($password, substr($password, 0, 2));
}
echo crypt3('123456'); // 12tir.zIbWQ3c
PHP
SHA-1
Этот алгоритм небезопасен по современным стандартам, работает во всех версиях.
{SHA} + результат SHA-1 (бинарная строка из 20-ти символов) закодированный в Base64.
function hash_sha1($password)
{
return '{SHA}' . base64_encode(sha1($password, true));
}
echo hash_sha1('123456'); // {SHA}fEqNCco3Yq9h5ZUglD3CZJT4lBs=
```
## Авторизация и выход
Реквизиты доступа к закрытой директории можно передать в URL:
```
https://логин:пароль@example.com/path
```
Если такие `URL` использовать в `src` изображений, скриптов и стилей, то работать они не будут, вызвав ошибку:
```Subresource requests whose URLs contain embedded credentials (e.g. `https://user:pass@host/`) are blocked.```
Завершение сеанса происходит по закрытию браузера, но не вкладки.
### На стороне JS
```js
document.execCommand("ClearAuthenticationCache");
```
A "basic" way
```js
var p = window.location.protocol + '//'
// current location must return 200 OK for this GET
window.location = window.location.href.replace(p, p + 'logout:password@')
```
An "asynchronous" way of doing the above is to do an AJAX call utilizing the logout username.
Example:
```js
(function(safeLocation){
var outcome, u, m = "You should be logged out now.";
// IE has a simple solution for it - API:
try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
// Other browsers need a larger solution - AJAX call with special user name - 'logout'.
if (!outcome) {
// Let's create an xmlhttp object
outcome = (function(x){
if (x) {
// the reason we use "random" value for password is
// that browsers cache requests. changing
// password effectively behaves like cache-busing.
x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
x.send("")
// x.abort()
return 1 // this is **speculative** "We are done."
} else {
return
}
})(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u ))
}
if (!outcome) {
m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
}
alert(m)
// return !!outcome
})(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)
```
Other way
```js
function logout(secUrl, redirUrl) {
if (bowser.msie) {
document.execCommand('ClearAuthenticationCache', 'false');
} else if (bowser.gecko) {
$.ajax({
async: false,
url: secUrl,
type: 'GET',
username: 'logout'
});
} else if (bowser.webkit) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", secUrl, true);
xmlhttp.setRequestHeader("Authorization", "Basic logout");
xmlhttp.send();
} else {
alert("Logging out automatically is unsupported for " + bowser.name
+ "\nYou must close the browser to log out.");
}
setTimeout(function () {
window.location.href = redirUrl;
}, 200);
}
```