Blog
How to add a captcha to your website
Captcha is the acronym for <Completely Automated Public Turing test to tell Computers and Humans Apart>.
It is a very popular technique that tries to do exactly what it's name implies: to tell humans and computers (bots) apart. This can be useful to websites in order to distinguish form submissions made by humans (legitimate), or form submissions made by bots (usually spam).
Before we set up an example, lets see how does captcha work.
On page load, we create a session named -for example- "captcha" and as a value we give it a series of alphanumeric characters, usually from 4 to 6.
This alphanumeric series we also present as a picture in our page, asking the user to copy the identified characters into a field. The field is also submitted as part of our <form> tag.
Upon submission, the script compares the characters of that field to the already created session named "captcha".
If the content matches the session's value we continue with the form process. If not, we stop any further process and return a "wrong captcha" error.
Pretty simple, isn't it?
Now lets see an example script of the above described function:
Let's start by checking if the session exists, and if yes, create a new one. Also set the number of characters our captcha is going to have.
<?php
session_start();
if(isset($_SESSION['captcha'])){
unset($_SESSION['captcha']);
}
$num_chars=5; // number of characters for captcha image
Now, let's specify the array of the characters that will form the series. Here we have excluded the letters "l,m,n,o" and the numbers "1 and 0" in order to avoid confusion. We proceed with the construction of the characters sequence and we store that in a session.
$characters=array_merge(range(2,9),range('a','k'),range('p','z')); // creating combination of numbers & alphabets
shuffle($characters); // shuffling the characters
$captcha_text="";
for($i=0;$i<$num_chars;$i++){ // getting the required random 5 characters
$captcha_text.=$characters[rand(0,count($characters)-1)];
}
$_SESSION['captcha'] = $captcha_text; // assigning the characters to the session
Now it's time to create the image. To do that we will use a canvas as background and a set of fonts. The font that we will use for this job is "Times New Yorker". We will place the file "times_new_yorker.ttf" in a folder named "font" and will include in in our script.
header("Content-type: image/png"); // setting the image type as png
$captcha_image=imagecreatetruecolor(120,32); // create the captcha image
$captcha_background=imagecolorallocate($captcha_image,225,238,221); // setting captcha background colour
$captcha_text_colour=imagecolorallocate($captcha_image,58,94,47); // setting cpatcha text colour
imagefilledrectangle($captcha_image,0,0,120,32,$captcha_background); // creating the rectangle
$font='font/times_new_yorker.ttf'; // setting the font path
imagettftext($captcha_image,22,0,14,25,$captcha_text_colour,$font,$captcha_text); // write the text
imagepng($captcha_image); // image created here
imagedestroy($captcha_image); // image destroyed
While most of the above steps are self-explanatory, I will go through them quickly:
We create a header specifying the image type (Here it's .png), and then we create the image.
Then we set the background color and the font color, and we create the rectangle that will contain the text.
Next we specify the font folder and we write the text.
That image we export as .png, and right after we destroy it since we don't need it anymore (it already shows in our page).
Now for the full code:
<?php
session_start();
if(isset($_SESSION['captcha'])){
unset($_SESSION['captcha']);
}
$num_chars=5; // number of characters for captcha image
$characters=array_merge(range(2,9),range('a','k'),range('p','z')); // creating combination of numbers & alphabets
shuffle($characters); // shuffling the characters
$captcha_text="";
for($i=0;$i<$num_chars;$i++){ // getting the required random 5 characters
$captcha_text.=$characters[rand(0,count($characters)-1)];
}
$_SESSION['captcha'] = $captcha_text; // assigning the characters to the session
header("Content-type: image/png"); // setting the image type as png
$captcha_image=imagecreatetruecolor(120,32); // create the captcha image
$captcha_background=imagecolorallocate($captcha_image,225,238,221); // setting captcha background colour
$captcha_text_colour=imagecolorallocate($captcha_image,58,94,47); // setting cpatcha text colour
imagefilledrectangle($captcha_image,0,0,120,32,$captcha_background); // creating the rectangle
$font='font/times_new_yorker.ttf'; // setting the font path
imagettftext($captcha_image,22,0,14,25,$captcha_text_colour,$font,$captcha_text); // write the text
imagepng($captcha_image); // image created here
imagedestroy($captcha_image); // image destroyed
?>
Now save this as "captcha.img.php" and place it in a folder which must be accessible by the web server.
In the same location also place the folder that contains the .ttf (font file).
To use it, we call it as the "src" in an <img> tag. Like this:
<img src="path/to/captcha_folder/captcha.img.php" />
On every page load the previous session and image are being destroyed, and we are presented with new ones.
Although not fully "bot-proof" this script will repell the vast majority of bots that will try to submit our form.
Some bots might manage to read the characters, but we want these characters to be easily readable by people too, so we can be a little flexible here.
Adjust the message size in Postfix
Postfix is one of the most popular MTAs (Mail Transport Agents). Due to its popularity, it is handling the mail for many different users and in many different kinds of environments. Therefore some of it's default settings may occasionally need some adjustments.
When browsing the logs of your mail server, have you ever seen something like that?
Sep 26 03:05:01 server01 postfix/local[56145]: A1B2AB123456: to=<root@server01.example.com>, orig_to=<root>, relay=local, delay=0.18, delays=0.08/0.03/0/0.07, dsn=5.2.2, status=bounced (cannot update mailbox /var/mail/root for user root. error writing message: File too large)
Or, did your users complaint that they cannot receive -hmm, these kinda ...larger- attachments?
Well that is because Postfix is adjusted by default to handle messages up to 10240000 bytes, or approximately 10MB. Now think that MIME encoding adds an overhead of 33.33% to the size of any attachment. That's because every 3 bytes are being increased by 1, to a total of 4 bytes. So one can easily understand that the default maximum allowed attachment size is about 7MB!
If we want to increase that -rather small number, all we have to do is login as root to our mail server and edit the Postfix main configuration file (main.cf), usually located in /etc/postfix
# vi /etc/postfix/main.cf
Then find the command message_size_limit and make it equal to a larger number. Maybe something like 100MB...
message_size_limit = 102400000
Save the file and exit.
Oh, and don't forget to restart (or reload) Postfix!
This small adjustment of 100 Megabytes limit minus the MIME overhead, means that our server should now be able to accept messages up to 70MB! Not bad huh?
Enable EPEL Repository on CentOS or RHEL box
EPEL stands for "Extra Packages for Enterprise Linux" and is a yum repository.
It provides hight quality additional software to accompany your RedHat distribution, such as:
- RedHat Enterprise Linux
- CentOS
- Fedora
Two methods are available to RedHat users for installing EPEL
YUM method
Use yum, the built-in command:
# yum install epel-release
RPM method
Mostly applicable to CentOS systems, we need to issue a command according to our distro.
CentOS 8
# yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
# dnf config-manager --set-enabled PowerTools
CentOS 7
# yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
CentOS 6
# yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
Enable the EPEL repository
In order to activate and start using the newly installed EPEL repository issue the command below
# yum --disablerepo=* --enablerepo=epel list all
The above command will show a (pretty long) list of the available software, that looks something like this:
....................................
zimg-devel.x86_64 2.9.3-1.el8 epel
zinnia.x86_64 0.06-46.el8 epel
zinnia-devel.x86_64 0.06-46.el8 epel
zinnia-doc.noarch 0.06-46.el8 epel
zinnia-perl.x86_64 0.06-46.el8 epel
zinnia-tomoe-ja.x86_64 0.06-46.el8 epel
zinnia-tomoe-zh_CN.x86_64 0.06-46.el8 epel
zinnia-utils.x86_64 0.06-46.el8 epel
zip.x86_64 3.0-23.el8 BaseOS
zlib.i686 1.2.11-16.el8_2 BaseOS
zlib-devel.i686 1.2.11-16.el8_2 BaseOS
zlib-devel.x86_64 1.2.11-16.el8_2 BaseOS
znc.x86_64 1.8.1-1.el8 epel
znc-devel.x86_64 1.8.1-1.el8 epel
znc-modperl.x86_64 1.8.1-1.el8 epel
znc-modpython.x86_64 1.8.1-1.el8 epel
znc-modtcl.x86_64 1.8.1-1.el8 epel
zork.x86_64 1.0.2-3.el8 epel
zsh.x86_64 5.5.1-6.el8_1.2 BaseOS
zsh-html.noarch 5.5.1-6.el8_1.2 AppStream
zsh-syntax-highlighting.noarch 0.7.1-1.el8 epel
zstd.x86_64 1.4.2-2.el8 AppStream
zswap-cli.x86_64 0.4.1-1.el8 epel
zvbi.x86_64 0.2.35-9.el8 epel
zvbi-devel.x86_64 0.2.35-9.el8 epel
zvbi-fonts.noarch 0.2.35-9.el8 epel
zziplib.i686 0.13.68-8.el8 AppStream
zziplib.x86_64 0.13.68-8.el8 AppStream
zziplib-utils.x86_64 0.13.68-8.el8 AppStream
Setup DKIM on Centos 7 server running Postfix
The DKIM (Domain Keys Identified Mail) is an email-signing method that utilizes two keys: one private and one public.
DKIM is applied by the mail server of the email recipient in order to verify the authenticity of the sender, and thus to reduce spam.
The workflow of DKIM is rather simple:
Upon setup at the mail server, we create one private and one public key. The public key is then copied to the zone file of our domain in the form of a TXT record. The private key -as the name implies- resides at the mail server in a private folder.
When a user sends an email, this message is signed by the mail agent (ie Postfix) according to the private key. This digital signature is attached to the headers of the email. Finally, the server of the recipient reads that signature and compares it to the public key (which is found in the domain's zone file). If keys and signature match, the message is considered legitimate and is moved to recipient's inbox.
In the following tutorial I will try to explain in 7 simple tasks how to implement DKIM signature in your mail server.
For simplicity purposes, lets assume that you are running RedHat CentOS 7 mail server, Postfix is already up and running, and that our domain name is "domain.com".
Task 1: Install OpenDKIM
For this task make sure you have installed and enabled the EPEL repository
# yum install opendkim
Task 2: Generate keys
# mkdir -p /etc/opendkim/keys/domain.com
# cd /etc/opendkim/keys/domain.com
# opendkim-genkey -r -d domain.com
Task 3: Set the right permissions
# chown -R opendkim:opendkim /etc/opendkim
# chmod go-rw /etc/opendkim/keys
Task 4: OpenDKIM configuration
# vi /etc/opendkim.conf
Make sure that the below parameters are as described
Mode sv
Socket inet:8891@localhost
Domain domain.com
#KeyFile /etc/opendkim/keys/default.private ### comment this line
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
Now edit the domain keys lists setting file:
# vi /etc/opendkim/KeyTable
and add the following line:
default._domainkey.domain.com domain.com:default:/etc/opendkim/keys/domain.com/default.private
Next, edit the signing table file
# vi /etc/opendkim/SigningTable
and make sure the following line appears:
*@domain.com default._domainkey.domain.com
Finally, edit the trusted hosts file
# vi /etc/opendkim/TrustedHosts
and add the hostnames that are permitted to send mail on behalf of your domain:
domain.com
mail.domain.com
Task 5: Postfix configuration
Now open the main configuration file of Postfix:
# vi /etc/postfix/main.cf
and add these lines at the end of the file:
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
Task 6: Start and enable the new service
We need to start OpenDKIM, make sure it starts automatically on boot, and finally restart Postfix
# service opendkim start
# chkconfig opendkim on
# service postfix restart
Task 7: Configure DNS zone file
Get the contents of file default.txt
# cat /etc/opendkim/keys/domain.com/default.txt
You will get an output similar to that one:
default._domainkey IN TXT ( "v=DKIM1; k=rsa; s=email; "
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdTtEqM8FqndiFYOderzljMMMqBdEp+wJKP+VUbhc9GigmK34ZjrSqqdKjIEWr2q9DvSVp1H1bZs4t050m0HZxJqknDz2yoDJ6W4mCaSCHesRde5V44V/L65Gqm/rvBz1d6CCp8A2515eveWrIAocOD6pKJ4tnXHz3uwV2ZtgQiQIDAQAB" ) ; ----- DKIM key default for domain.com
Mark and copy the contents of your file.
Next at your DNS server open your domain's zone file, and paste the copied content at the very end of it.
If necessary, restart the DNS service.
That's it. You are now running the DKIM service!
IMPORTANT: In case you serve multiple domains
If your server is shared between more that one domain names, repeat tasks 2 and 3 for each one of your domains.
Then edit the KeyTable and SigningTable to complete the procedure.
vi /etc/opendkim/KeyTable
and for each one of your domains add a line as per example:
default._domainkey.domain1.com domain1.com:default:/etc/opendkim/keys/domain1.com/default.private
default._domainkey.domain2.com domain2.com:default:/etc/opendkim/keys/domain2.com/default.private
default._domainkey.domain3.com domain3.com:default:/etc/opendkim/keys/domain3.com/default.private
and
vi /etc/opendkim/SigningTable
here too, for each one of your domains, add a line as shown below:
*@domain1.com default._domainkey.domain1.com
*@domain2.com default._domainkey.domain2.com
*@domain3.com default._domainkey.domain3.com
Last but not least, for every domain repeat Task 7 and at the end, don't forget to restart the services!
Optional task: Verify the service
In case you are wondering, you can verify the proper operation of your new DKIM installation by visiting:
Or you can just send an email to: ping@tools.mxtoolbox.com and an automated reply with email status details, will arrive shortly to your Inbox.
It is an excellent mail-deliverability tool and very easy to use.
Just send an email to the specified address (ping@tools.mxtoolbox.com -at the time of the writing), type your email address to the corresponding box, and press "Search" to view your results.
Using a Webfont with DOMPDF
DOMPDF is a reliable class to convert HTML documents to PDF. It is compatible with most CSS 2.1 rules. The resulting pdf has remarkable quality, which is comparable to other paid (and pricey) solutions. By adding custom fonts, we will not only be able to display UTF-8 characters, such as Greek, Cyrillic etc, but also we will give our document this personalized character.
First you should install DOMPDF. This can be done by downloading and decompressing it to a directory of your choice of a php-enabled web server.
If you haven't already downloaded DOMPDF, you can get it from here:
https://github.com/dompdf/dompdf/releases
These fonts are embedded within the release:
- Courier (Normal, Bold, Oblique, and BoldOblique variants)
- Helvetica (Normal, Bold, Oblique, and BoldOblique variants)
- Times (Normal, Bold, Oblique, and BoldOblique variants)
- Symbol
- ZapfDingbats
and as of release 0.6.0, DejaVu TrueType fonts are also included.
And now the fun begins!
So, lets say that we want to embed the "Roboto" webfont (regular and bold).
First, change to the lib directory of your dompdf installation:
cd /path/to/dompdf/lib
Download load_font.php inside that (lib) directory.
curl -o load_font.php https://raw.githubusercontent.com/dompdf/utils/master/load_font.php
Open load_font.php with your favorite text editor.
Replace
require_once "autoload.inc.php";
To:
require_once 'dompdf/autoload.inc.php';
Download Roboto font (regular and bold variations) from your favorite source, and place both .ttf under the same lib folder and in subfolder fonts.
lib/fonts/
This is the generic way to run the load_font.php script
php load_font.php new_font_family ./path/to/new_font.ttf
...or in our case:
php load_font.php Roboto ./fonts/Roboto-Regular.ttf ./fonts/Roboto-Bold.ttt
The new webfont is now embedded in DOMPDF!
To use it, specify it in the head tag of your html code:
<?php
/* make sure you escape the double quotes like this: \" */
$page="<html>
<head>
<style>
body{font-family: 'Roboto', sans-serif;}
... rest of css rules here ...
</style>
... your other 'head' tags here ...
</head>
<body>
... HTML CONTENT HERE ...
</body>
</html>";
/* then use DOMPDF to make the conversion: */
require_once "lib/dompdf/autoload.inc.php";
use Dompdf\Dompdf;
$dompdf=new Dompdf();
$dompdf->loadHtml($page, "UTF-8");
$dompdf->setPaper("A4", "portrait");
$dompdf->set_option('isFontSubsettingEnabled', true);
$dompdf->set_option('defaultMediaType', 'all');
$dompdf->render();
$dompdf->stream(); // stream the pdf in browser
The above commands for DOMPDF are pretty much self explainatory.
For any further clarifications, refer to the author's page:
https://github.com/dompdf/dompdf
Web Hosting and Hacker Attacks
The term web-hosting means the storage of a website on a server's hard drive. A website is basically a folder which contains various files and subfolders. By copying it to a Web Server, we are actually making it available to everyone on the Internet. All it takes to retrieve the contents of this folder, is a browser that will take various files and will combine them into a human-readable web page.
A server consists of the hardware, which is quite similar to the hardware of a Personal Computer, the Operating System -which is the piece of software that manages computer's resources and facilitates it's communication with the "outside world", the Services -which determine and regulate all the individual tasks that the server is carrying out, and some other programs such as antivirus, firewall etc.
Although web hosting at first might sound as a simple and easy task, turns out that not only is not, but also carries a lot of complexity and requires the skills of a specialist called "Systems Administrator". That is of course if we don't want to turn our Server into a playground for hackers!
The Systems Administrator (or SysAdmin), will not only choose the correct software bundle (operating system, services and programs), but occasionally might also suggest hardware modifications that he/she thinks are necessary for the smooth operation of the server.
Once the server is ready, the proper software is installed, and passed all the security audits, it goes directly into production. If it's a "Web Server" we create the users and upload the websites that it will host. Once a website is online, it exposes the IP address of it's hosting server, and a non-stop series of hacker attacks begins. Hackers usually employ mass attack programs (also known as bots) to locate a server with a certain voulnerability, and then proceed to hacking that machine. Among their goal usually is to "deface" the hosted websites, or to steal information from their database.
The most important and difficult job of a SysAdmin is to monitor these attacks, find software vulnerabilities and install the appropriate software updates and patches, that will eliminate these vulnerabilities.
A very popular attack is the DoS (Denial of Service). It was trending some years ago, when most machines were not software-ready to repell such attacks.
Every server has a certain amount of resources that can serve so many requests per minute. A DoS attack creates numerous requests from completely different IP addresses located all around the world, that a server cannot handle and just "blocks". This does not usually impose a permanent damage to the server or the hosted websites, but once it becomes "targeted" is a matter of minutes or even seconds before it goes "down".
The repelling of various attacks is a never ending task and nowadays rather automated for servers that host mainstream websites. The hard part begins when we host websites of "special interest" such as political or religious sites which except from their many fans will also attract a good number of opponents!
In that case is very much anticipated an organized hacking attack often by one or more -and often quite competent- attackers, that can really make SysAdmin's life very difficult.
Validating European VAT numbers with PHP
As time passes, accounting laws become more and more strict, making electronic invoicing applications even more complicated. Over the last years and especially in the European Union, the issue of phony invoices has been increased, forcing the countries-members to adopt new laws in order to fight that.
One handy tool for us developers is VIES (VAT Information Exchange Service). It's a free of charge service that is maintained by EU, and can be used to check the validity of every VAT number of European Union. Queries can be run either by using their web interface or programmatically with the use of a Simple Objects Access Protocol (SOAP) API.
The implementation is relatively easy, and it was one of the things that motivated us to include it during the development of AssetCRM. And because any form of knowledge should be spread, I thought to post a simple example of extracting information from VIES using PHP language. Luckily in VIES website there is a well documented technical information which will guide us through our first steps in creating a script:
We'll define constant VIES_URL with the actual VIES communication URL.
It will be quite handy to build the script inside a function, since this will facilitate it's later usage:
<?php
DEFINE ( 'VIES_URL', 'http://ec.europa.eu/taxation_customs/vies/services/checkVatService' );
function viesCheckVAT($countryCode, $vatNumber, $timeout = 30) {
$response = array ();
$pattern = '/<(%s).*?>([\s\S]*)<\/\1/';
$keys = array (
'countryCode',
'vatNumber',
'requestDate',
'valid',
'name',
'address'
);
$content = "<s11:Envelope xmlns:s11='http://schemas.xmlsoap.org/soap/envelope/'>
<s11:Body>
<tns1:checkVat xmlns:tns1='urn:ec.europa.eu:taxud:vies:services:checkVat:types'>
<tns1:countryCode>%s</tns1:countryCode>
<tns1:vatNumber>%s</tns1:vatNumber>
</tns1:checkVat>
</s11:Body>
</s11:Envelope>";
$opts = array (
'http' => array (
'method' => 'POST',
'header' => "Content-Type: text/xml; charset=utf-8; SOAPAction: checkVatService",
'content' => sprintf ( $content, $countryCode, $vatNumber ),
'timeout' => $timeout
)
);
$ctx = stream_context_create ( $opts );
$result = file_get_contents ( VIES_URL, false, $ctx );
if (preg_match ( sprintf ( $pattern, 'checkVatResponse' ), $result, $matches )) {
foreach ( $keys as $key )
preg_match ( sprintf ( $pattern, $key ), $matches [2], $value ) && $response [$key] = $value [2];
}
return $response;
}
?>
Now to use the above function.
Say we want to check a German (DE) VAT number (1234567890). We call the function like that:
viesCheckVAT("DE","1234567890"); // which of course it will return "1234567890" as an invalid VAT number ))
The way the above function works, is quite simple:
First we create the arrays and variables, and then we construct the "xml" file that we send to the defined VIES_URL using header and other options specified in array "opts". Finally return the response by looping over the response keys, with a good ol' "foreach" loop.
This function was originally published by Eugen Mihailescu.
You can see the above script in action by clicking VAT number validation
CSS text fading effect
The fading-text effect is a handy solution when we have a text that doesn't fit a given container. In this case, we want to fit as much text necessary to fill the container, and also to apply a fade-out effect in the bottom -just before the end of the containing div.
It's something like "our Services" part on our Homepage, where there are three fixed-size divs, which get their contents from other webpages and -due to aesthetical reasons- these contents should remain contained neatly within these divs.
Lets start by creating the HTML environment:
<div class="cont">
<div class="text">
<div class="ovl"></div>
<p class="par">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
We have a container with class "cont", which contains an inner div with class "text" (which will obviously contain our text).
Within the "text" div, we find another div with class "ovl" -which will serve as an overlay, and finally a paragraph (class "par") which contains the text itself. As a text we choose a paragraph of the well-known "Lorem ipsum" placeholding text.
Now we need some help from CSS in order to achieve our goal, which is done like that:
.cont{
margin:0 auto;
width: 440px;
height: 300px;
border: 1px solid red;
padding: 10px;
}
.text{
position: relative;
margin: 0 auto;
width: 220px;
height: 220px;
border: 1px solid black;
overflow: hidden;
}
.ovl{
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
box-shadow: inset 0px -56px 26px 0px rgba(255,255,255,1);
}
.par{
margin-top:0px;
padding:10px;
}
While most CSS rules are purely for decoration, it's this one rule that does the job and creates this fading effect:
box-shadow: inset 0px -56px 26px 0px rgba(255,255,255,1);
We apply shadow to the "ovl" div, which is inset (inward direction), 0 pixels horizontal lenght, -56 pixels vertical lenght, we blur it a bit with 26 pixels of radius, 0 pixels of spreading radius and our color of choice is white:
#FFFFFF;
or given in rgba mode:
rgba(255,255,255,1);
Of course the above numbers and/or color need to be altered depending on the case each time, but that should give you a rough idea as to how "box-shadow" is working. For the color, a "rule of thumb" is that it should resemble the background color of the container. Check the middle column of the footer of our website, where there's a similar effect but the box-shadow color is dark grey (#252525).
To see the above tutorial in action, check out this Fiddle.