16/10/2018 - LINUX, PHP, NGINX
Bu örnekte X.509 Public Key Infrastructure (PKI) standardıyla istemci isteğini doğrulayacağız. Bu kimlik doğrulama standardının Basic, Digest, JWT, OAuth vb. gibi diğer türlere göre kullanılmasının temel avantajı, istemcinin sunucuya bir kullanıcı adı veya şifre göndermemesidir. Bu, man-in-the-middle attack (MITM) saldırısını (saldırgan gizlice iletiyi gizler ve muhtemelen iletişimi değiştirir) zorlaştırır. İstemci, kişiselleştirilmiş bir sertifika ve sunucuya bir anahtar gönderir. Genel olarak giriş kimlik bilgilerini çalmak bir sertifika çalmaktan daha kolaydır.
Sistem Debian 8.10'a (Jessie) dayanıyor ve PHP 7.2.6 ile birlikte Nginx Server 1.6.2 kullanıyor.
İstemci, herhangi bir kimlik doğrulaması yapmadan tüketebileceği şekilde sahte bir PHP API'sı kurun.
$ mkdir /srv/www/football
$ nano /srv/www/football/index.php
# This is the file content
echo PHP_EOL;
print_r($_SERVER);
echo PHP_EOL;
$ sudo nano /etc/nginx/sites-available/football
# This is the file content
server {
listen 83;
root /srv/www/football;
server_name football.dev;
location / {
index index.php index.html index.htm;
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
error_log /var/log/nginx/football_error.log;
access_log /var/log/nginx/football_access.log;
}
$ sudo ln -s /etc/nginx/sites-available/football /etc/nginx/sites-enabled/football
$ sudo service nginx restart
$ curl http://192.168.99.20:83
# This is the result (Access to http://192.168.99.20:83/ via browser produces the similar output)
Array
(
[USER] => www-data
[HOME] => /var/www
[HTTP_ACCEPT] => */*
[HTTP_HOST] => 192.168.99.20:83
[HTTP_USER_AGENT] => curl/7.38.0
[REDIRECT_STATUS] => 200
[SERVER_NAME] => football.dev
[SERVER_PORT] => 83
[SERVER_ADDR] => 192.168.99.20
[REMOTE_PORT] => 40541
[REMOTE_ADDR] => 192.168.99.20
[SERVER_SOFTWARE] => nginx/1.6.2
[GATEWAY_INTERFACE] => CGI/1.1
[SERVER_PROTOCOL] => HTTP/1.1
[DOCUMENT_ROOT] => /srv/www/football
[DOCUMENT_URI] => /index.php
[REQUEST_URI] => /
[SCRIPT_NAME] => /index.php
[CONTENT_LENGTH] =>
[CONTENT_TYPE] =>
[REQUEST_METHOD] => GET
[QUERY_STRING] =>
[SCRIPT_FILENAME] => /srv/www/football/index.php
[PATH_INFO] =>
[FCGI_ROLE] => RESPONDER
[PHP_SELF] => /index.php
[REQUEST_TIME_FLOAT] => 1539377667.6224
[REQUEST_TIME] => 1539377667
)
Aşağıdaki tüm komutları sunucuda çalıştırın. İstemci anahtarını ve sertifikayı, API'yi kullanması için sahibine güvenilir bir şekilde verin. Not: İsteğe bağlı olarak, bir pem
dosyası oluşturmak için istemci anahtarını ve sertifikayı birleştirebilir ve istemcilerin iki farklı dosya kullanmasını istemek yerine kullanmasını sağlayabilirsiniz. Eğer Certificate revocation list (CRL) kullanmak isterseniz pem
kullanmanız gerekecektir ama biz şu anda kullanmayacağız. Bazen herhangi bir sebepten dolayı son kullanma tarihinden önce sertifikaları iptal etmek isteriz, bu yüzden bu önemli bir özelliktir.
Bunlar kendi kendimize istemci sertifikalarını imzalamak için kullanılacaktır. CA, gerçek hayat örneklerinde, VeriSign, Thawte, Symantec, Comodo vb. gibi şirketlerdir ama biz bu seferlik kendi kendimize CA olacağız. Bu tarz şirketler X.509 sertifikalarını verirler.
$ sudo mkdir -p /etc/nginx/ssl/key
$ sudo mkdir -p /etc/nginx/ssl/certificate
$ sudo openssl genrsa -out /etc/nginx/ssl/key/football_ca.key 4096
Generating RSA private key, 4096 bit long modulus
............++
.....++
65537 (0x10001)
$ sudo openssl req -new -x509 -days 365 -key /etc/nginx/ssl/key/football_ca.key -out /etc/nginx/ssl/certificate/football_ca.crt
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:UK
State or Province Name (full name) [Some-State]:Greater London
Locality Name (eg, city) []:City of London
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Football Stats Ltd.
Organizational Unit Name (eg, section) []:Tech
Common Name (e.g. server FQDN or YOUR name) []:football.dev
Email Address []:info@football.dev
$ ls -l /etc/nginx/ssl/key
-rw-r--r-- 1 root root 3247 Oct 16 22:13 football_ca.key
$ ls -l /etc/nginx/ssl/certificate
-rw-r--r-- 1 root root 2183 Oct 16 22:18 football_ca.crt
Bir sertifika imzalama talebi (CSR), bir dijital kimlik sertifikası başvurusunda bulunmak için başvuru sahibinden bir sertifika yetkilisine (CA) gönderilen bir mesajdır.
$ sudo openssl genrsa -out /etc/nginx/ssl/key/football.key 1024
Generating RSA private key, 1024 bit long modulus
...++++++
.........................++++++
65537 (0x10001)
$ sudo openssl req -new -key /etc/nginx/ssl/key/football.key -out /etc/nginx/ssl/certificate/football.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:UK
State or Province Name (full name) [Some-State]:Greater London
Locality Name (eg, city) []:City of London
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Football Stats Ltd.
Organizational Unit Name (eg, section) []:Tech
Common Name (e.g. server FQDN or YOUR name) []:football.dev
Email Address []:info@football.dev
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:123123
An optional company name []:Footy
$ ls -l /etc/nginx/ssl/key
-rw-r--r-- 1 root root 3247 Oct 16 22:13 football_ca.key
-rw-r--r-- 1 root root 887 Oct 16 22:24 football.key
$ ls -l /etc/nginx/ssl/certificate
-rw-r--r-- 1 root root 2183 Oct 16 22:18 football_ca.crt
-rw-r--r-- 1 root root 798 Oct 16 22:27 football.csr
Bu işlemi production ortamında yapmayın. Bu işlem "riskli" kabul edildiği için, onun yerine sertifika yetkilisinden (VeriSign, Thawte, Symantec, Comodo vs) bir tane alın. Her ne kadar kendinden imzalı SSL sertifikalar istemcilerin giriş kimlik bilgilerini ve diğer hassas verileri şifrelese bile, web sunucularında bir güvenlik uyarısı göstermesine sebep olurlar. Aynı uyarı cURL ve Guzzle kullanırkende oluşur. Bunun nedeni, sertifikanın güvenilir bir sertifika yetkilisi (CA) tarafından doğrulanmadığıdır. Sonuç olarak bu tür bir uyarı, kullanıcılarınızın sitenizi terk etmesine neden olur.
$ sudo openssl x509 -req -days 365 -in /etc/nginx/ssl/certificate/football.csr -CA /etc/nginx/ssl/certificate/football_ca.crt -CAkey /etc/nginx/ssl/key/football_ca.key -set_serial 01 -out /etc/nginx/ssl/certificate/football.crt
Signature ok
subject=/C=UK/ST=Greater London/L=City of London/O=Football Stats Ltd./OU=Tech/CN=football.dev/emailAddress=info@football.dev
Getting CA Private Key
$ ls -l /etc/nginx/ssl/certificate
-rw-r--r-- 1 root root 2183 Oct 16 22:18 football_ca.crt
-rw-r--r-- 1 root root 1529 Oct 16 22:33 football.crt
-rw-r--r-- 1 root root 798 Oct 16 22:27 football.csr
İstemci anahtarı, gerçek istemciye güvenilir bir şekilde gönderilir, böylece API'mızı tüketirken kullanabilirler.
$ sudo openssl genrsa -out /etc/nginx/ssl/key/client_a.key 1024
Generating RSA private key, 1024 bit long modulus
.......++++++
.++++++
65537 (0x10001)
$ sudo openssl req -new -key /etc/nginx/ssl/key/client_a.key -out /etc/nginx/ssl/certificate/client_a.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:UK
State or Province Name (full name) [Some-State]:Greater Manchester
Locality Name (eg, city) []:City of Manchester
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Client A Ltd.
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:client_a.com
Email Address []:info@client_a.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:aaaaaa
An optional company name []:CA Ltd.
$ ls -l /etc/nginx/ssl/key
-rw-r--r-- 1 root root 887 Oct 16 22:36 client_a.key
-rw-r--r-- 1 root root 3247 Oct 16 22:13 football_ca.key
-rw-r--r-- 1 root root 887 Oct 16 22:24 football.key
$ ls -l /etc/nginx/ssl/certificate
-rw-r--r-- 1 root root 802 Oct 16 22:40 client_a.csr
-rw-r--r-- 1 root root 2183 Oct 16 22:18 football_ca.crt
-rw-r--r-- 1 root root 1529 Oct 16 22:33 football.crt
-rw-r--r-- 1 root root 798 Oct 16 22:27 football.csr
İstemci sertifikası, gerçek istemciye güvenilir bir şekilde gönderilir, böylece API'mızı tüketirken kullanabilirler.
$ sudo openssl x509 -req -days 365 -in /etc/nginx/ssl/certificate/client_a.csr -CA /etc/nginx/ssl/certificate/football_ca.crt -CAkey /etc/nginx/ssl/key/football_ca.key -set_serial 01 -out /etc/nginx/ssl/certificate/client_a.crt
Signature ok
subject=/C=UK/ST=Greater Manchester/L=City of Manchester/O=Client A Ltd./OU=Sales/CN=client_a.com/emailAddress=info@client_a.com
Getting CA Private Key
$ ls -l /etc/nginx/ssl/certificate
-rw-r--r-- 1 root root 1533 Oct 16 22:45 client_a.crt
-rw-r--r-- 1 root root 802 Oct 16 22:40 client_a.csr
-rw-r--r-- 1 root root 2183 Oct 16 22:18 football_ca.crt
-rw-r--r-- 1 root root 1529 Oct 16 22:33 football.crt
-rw-r--r-- 1 root root 798 Oct 16 22:27 football.csr
İstemci anahtarını ve sertifikayı pem
dosyası oluşturmak için birleştirebilir ve istemcilerin iki farklı dosya kullanmasını istemek yerine bir dosya kullanmasını sağlayabilirsiniz. Bu sadece yukarıda belirttiğimiz gibi Certificate revocation list (CRL) kullanacaksanız önemlidir. Gizlilik gelişmiş posta (PEM) bir Base64 kodlanmış sertifikadır. Web sunucuları için PEM sertifikaları sıklıkla kullanılır. --cert
olarak kullanılır, --key
olarak değil.
$ sudo bash -c 'cat /etc/nginx/ssl/certificate/client_a.crt /etc/nginx/ssl/key/client_a.key > /etc/nginx/ssl/certificate/client_a.pem'
Nginx yapılandırmasını güncelleyin, böylece API'ye girmeden önce X.509 kimlik doğrulaması sunucu düzeyinde yapılır.
server {
listen 83;
root /srv/www/football;
server_name football.dev;
listen 443;
ssl on;
ssl_certificate /etc/nginx/ssl/certificate/football.crt;
ssl_certificate_key /etc/nginx/ssl/key/football.key;
ssl_client_certificate /etc/nginx/ssl/certificate/football_ca.crt;
ssl_verify_client optional; # Set to "on" if you only allow authenticated requests
location / {
index index.php index.html index.htm;
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify;
fastcgi_param SSL_CLIENT_S_DN $ssl_client_s_dn;
include fastcgi_params;
}
error_log /var/log/nginx/football_error.log;
access_log /var/log/nginx/football_access.log;
}
Sertifika geçersiz olduğunda API'ye ulaşılmasını önlemek istiyorsanız, isteğe bağlı olarak yukarıdaki yapılandırmanın location ~ \.php$
bloğuna aşağıdaki kodu ekleyebilirsiniz.
if ($ssl_client_verify != SUCCESS) {
return 403;
}
$ sudo nginx -c /etc/nginx/nginx.conf -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo service nginx restart
İstemciye kendi sertifika client_a.crt
ve anahtarını client_a.key
vererek, API'yi kullanmasını sağlayın.
Şu anda istemcinin bilgisayarındayız ve sunucu bilgisayarındaki API'yi kullanıyoruz. Aşağıdaki SSL_CLIENT_VERIFY
ve SSL_CLIENT_S_DN
anahtarlarına dikkat edin. Kendinden imzalı SSL sertifikaları kullanıldığımız için --insecure
ekini kullanmalıyız. Aksi takdirde curl: (60) SSL certificate problem: self signed certificate
hatası alırız.
# VALID
$ curl --insecure --key /srv/www/client_a.key --cert /srv/www/client_a.crt https://192.168.99.20:83
# OR pem version
# $ curl --insecure --cert /srv/www/client_a.pem https://192.168.99.20:83
Array
(
[USER] => www-data
[HOME] => /var/www
[HTTP_ACCEPT] => */*
[HTTP_HOST] => 192.168.99.20:83
[HTTP_USER_AGENT] => curl/7.38.0
[SSL_CLIENT_VERIFY] => SUCCESS
[SSL_CLIENT_S_DN] => /C=UK/ST=Greater Manchester/L=City of Manchester/O=Client A Ltd./OU=Sales/CN=client_a.com/emailAddress=info@client_a.com
[REDIRECT_STATUS] => 200
[SERVER_NAME] => football.dev
[SERVER_PORT] => 83
[SERVER_ADDR] => 192.168.99.20
[REMOTE_PORT] => 42553
[REMOTE_ADDR] => 192.168.99.30
[SERVER_SOFTWARE] => nginx/1.6.2
[GATEWAY_INTERFACE] => CGI/1.1
[HTTPS] => on
[SERVER_PROTOCOL] => HTTP/1.1
[DOCUMENT_ROOT] => /srv/www/football
[DOCUMENT_URI] => /index.php
[REQUEST_URI] => /
[SCRIPT_NAME] => /index.php
[CONTENT_LENGTH] =>
[CONTENT_TYPE] =>
[REQUEST_METHOD] => GET
[QUERY_STRING] =>
[SCRIPT_FILENAME] => /srv/www/football/index.php
[PATH_INFO] =>
[FCGI_ROLE] => RESPONDER
[PHP_SELF] => /index.php
[REQUEST_TIME_FLOAT] => 1539727722.2362
[REQUEST_TIME] => 1539727722
)
# MALFORMED certificate
$ curl --insecure --key /srv/www/client_a.key --cert /srv/www/client_a.crt https://192.168.99.20:83
curl: (58) unable to use client certificate (no key found or wrong pass phrase?)
# WRONG certificate
$ curl --insecure --key /srv/www/client_a.key --cert /srv/www/client_x.crt https://192.168.99.20:83
curl: (58) unable to set private key file: '/srv/www/client_a.key' type PEM
# WRONG key
$ curl --insecure --key /srv/www/client_x.key --cert /srv/www/client_a.crt https://192.168.99.20:83
curl: (58) unable to set private key file: '/srv/www/client_x.key' type PEM