mika56 / php-spf-check Goto Github PK
View Code? Open in Web Editor NEWSimple library to check an IP address against a domain's SPF record
License: MIT License
Simple library to check an IP address against a domain's SPF record
License: MIT License
dns_get_record() trips a warning, rather than returning false. Appears to be a known PHP issue: https://bugs.php.net/bug.php?id=73149 (although I'm running 7.2, not 5.6). Work-around: Add @ to dns_get_record() in mika56/spfcheck/src/DNSRecordGetter.php:24
As per RFC7208, the library wrongly handles includes. Include should only match Pass and return PermError on none. Currently it simply forwards the request to the included domain.
Consider the following records :
domaina.com IN TXT "v=spf1 include:domainb.com domainc.com -all"
domainb.com IN TXT "v=spf1 -all"
domainc.com IN TXT "v=spf1 +127.0.0.1 -all"
The result for 127.0.0.1 on domain domaina.com is Fail (coming from domainb.com) instead of Pass (from domainc.com)
Hi,
is it possible to check manuelly the spf by comparing string and ip and get result if pass or fail or none ...
exemple :
check
v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all
against
108.177.8.0
should return SPF PASS
This time I have a problem with my php 5.6.30
I tested on 5.4.16 and it is ok.
<?php
require 'lib/vendor/autoload.php';
use Mika56\SPFCheck\SPFCheck;
use Mika56\SPFCheck\DNSRecordGetter;
///////////////////////////////////////////////////////////////////////////////////////
$checker = new SPFCheck(new DNSRecordGetter());
$result = $checker->isIPAllowed('83.206.186.9', 'etam.biz');
print var_dump($result);
?>
[24-Mar-2017 17:47:03 Europe/Paris] PHP Warning: dns_get_record(): A temporary server error occurred. in /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/DNSRecordGetter.php on line 45
[24-Mar-2017 17:47:03 Europe/Paris] PHP Fatal error: Uncaught exception 'Mika56\SPFCheck\Exception\DNSLookupException' in /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/DNSRecordGetter.php:47
Stack trace:
#0 /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/SPFCheck.php(252): Mika56\SPFCheck\DNSRecordGetter->resolveA('cme.rc.etam.com')
#1 /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/SPFCheck.php(123): Mika56\SPFCheck\SPFCheck->ipMatchesPart('83.206.186.9', 'mx', 'etam.biz')
#2 /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/SPFCheck.php(87): Mika56\SPFCheck\SPFCheck->doCheck('83.206.186.9', 'etam.biz')
#3 /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/SPFCheck.php(67): Mika56\SPFCheck\SPFCheck->doIsIPAllowed('83.206.186.9', 'etam.biz', true)
#4 /apache_sites/jbm/titi.php(10): Mika56\SPFCheck\SPFCheck->isIPAllowed('83.206.186.9', 'etam.biz')
#5 {main}
thrown in /apache_sites/jbm/lib/vendor/mika56/spfcheck/src/DNSRecordGetter.php on line 47
mymail.pprgroup.net have no TXT record.
var_dump($checker->isIPAllowed('217.109.67.84', 'mymail.pprgroup.net'));
return "TE"
It should be "NO" I think
https://tools.ietf.org/html/rfc4408#section-2.5.1
Original message from @Pascal76
<?php
use Mika56\SPFCheck\SPFCheck;
use Mika56\SPFCheck\DNSRecordGetter;
require('vendor/autoload.php');
$checker = new SPFCheck(new DNSRecordGetter()); // Uses php's dns_get_record method for lookup.
var_dump($checker->isIPAllowed('172.82.227.233', 'm1.email.samsung.com'));
?>
=> string(2) "PE"
dig +short TXT m1.email.samsung.com |grep spf
"v=spf1 redirect=__spf.campaign.adobe.com"
There is a problem for this domain begining with 2 "_" with php < 7
if (version_compare(PHP_VERSION, '7', '>=')) {
if (!filter_var($domain, FILTER_VALIDATE_DOMAIN)) {
return false;
}
} else {
/** @link http://stackoverflow.com/a/4694816/2898156 */
if (!(preg_match('/^([_a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i', $domain)
&& preg_match('/^.{1,253}$/', $domain)
&& preg_match('/^[^\.]{1,63}(\.[^\.]{1,63})*$/', $domain))
) {
return false;
}
}
Currently we don't have any tests to test DNSRecordGetter
. We can use PHPUnit Bridge from symphony framework to mock DNS records. To test dns_get_record
we can use:
DnsMock::withMockedHosts(array(
'example.com' => array(
array(
'type' => 'A',
'ip' => '1.2.3.4',
)
),
));
The check process stops at the first of several includes if there is an error.
This is incorrect implemented see: https://tools.ietf.org/html/rfc7208#section-5.2
RFC 7208 says:
When evaluating the "mx" mechanism, the number of "MX" resource records queried is included in the overall limit of 10 mechanisms/modifiers that cause DNS lookups as described above.
But countRequest()
is only called once, when the MX records are looked up (but not for each record returned that causes an A lookup)
PHP-SPF-Check/src/SPFCheck.php
Lines 243 to 248 in d5b0aa4
There is a check that there isn't more than 10 MX records returned, which matches the following sentence in the RFC ("In addition to that limit, the evaluation of each "MX" record MUST NOT result inmquerying more than 10 address records"), but the lookup of an MX record seems to only count for one DNS request in total (as far as countRequest()
goes).
An example of a SPF record that is accepted that should not be is something like:
v=spf1 mx include:foo.example.com include:bar.example.com
with nine MX records attached to the same domain (should be over the limit as defined in the RFC, as it causes 11 DNS lookups, but this library will only count 3 - before includes are evaluated further).
getIPStringResult("52.70.139.219", "pitt.edu")
leads to an preg_split(): Argument #2 ($subject) must be of type string error.
SPF has an official test suite: http://www.openspf.org/svn/project/test-suite/
Might be nice to implement, instead of the current incomplete suite
Hi Mika
can you check that please ?
Pascal
It would be nice to have details
ex1: what validated the spf (ip4:xxx / a / mx / include => ip4:xxx ...)
ex2: full list of authorized IPs + others non IP which are authorized too
Please allow symfony/phpunit-bridge
^5.0, thanks! :)
Hi there. Thanks for the great lib. We are just upgrading our instance from 1.1.6 to latest and see there's a little bit of architectural change. I've gone through the Upgrade.md and sorted all of that.
We have a slightly unconventional implementation where we want to control our own request limits. So under 1.1.6 we extended your DnsRecordGetter and used $requestCount there to do that.
use Mika56\SPFCheck\DNS\DNSRecordGetter as BaseDNSRecordGetter;
use Mika56\SPFCheck\Exception\DNSLookupLimitReachedException;
class DNSRecordGetter extends BaseDNSRecordGetter
{
public const LIMIT = 20;
public function countRequest()
{
if (++$this->requestCount > self::LIMIT) {
throw new DNSLookupLimitReachedException();
}
}
public function getRequestCount(): int
{
return $this->requestCount;
}
}
I see that all of this stuff has moved from old src/DNSRecordGetter.php to the new src/DNS/Session.php class. So this obviously errors due to requestCount not existing.
What's the best way to do this now? It looks like the Result object contains a Session object. And I see on the Readme that getIPStringResult() returns a Result object. Should I just access this property that way? Or is there a better way to do the above entirely?
Thanks again.
Hi @Mika56 ,
I'm running into a problem with an SPF record that has +include:universalspf.org
If you look at the SPF record of universalspf.org
it has a value of v=spf1 a:%{i}._o.%{o}.y.0.universalspf.org -all
.
I've never seen an SPF record like this and it seems to cause problems with this SPF checking library.
It gets to like 45 of MacroUtils.php
and when it calls $query->getSender()
the value is null
and this causes explode
to fail.
Hi!
We had encountered problem with wrong SPF - one of our clients made spf like this:
domain.com TXT "spf=v1 include:domain.com ~all"
Of course it caused endless recursion. Due to this bug/feature https://bugs.php.net/bug.php?id=43187 it's imossible to catch this outside library, so it needs some tweaking in library itself.
Currently getting following dependency error on Laravel and Lumen 9 frameworks. Is it possible to update symfony/http-foundation to ^6.0 ?
`Problem 1
- mika56/spfcheck 1.0.0 requires symfony/http-foundation 2.8.* -> found symfony/http-foundation[v2.8.0-BETA1, ..., 2.8.x-dev] but the package is fixed to v6.0.6 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- mika56/spfcheck[1.0.1, ..., 1.1.2] require symfony/http-foundation 2.8.* || ^3.0 -> found symfony/http-foundation[v2.8.0-BETA1, ..., 2.8.x-dev, v3.0.0-BETA1, ..., 3.4.x-dev] but the package is fixed to v6.0.6 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- mika56/spfcheck[1.1.3, ..., 1.1.5] require symfony/http-foundation 2.8.* || ^3.0 || ^4.0 -> found symfony/http-foundation[v2.8.0-BETA1, ..., 2.8.x-dev, v3.0.0-BETA1, ..., 3.4.x-dev, v4.0.0-BETA1, ..., 4.4.x-dev] but the package is fixed to v6.0.6 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- mika56/spfcheck[dev-master, 1.1.6] require symfony/http-foundation 2.8.* || ^3.0 || ^4.0 || ^5.0 -> found symfony/http-foundation[v2.8.0-BETA1, ..., 2.8.x-dev, v3.0.0-BETA1, ..., 3.4.x-dev, v4.0.0-BETA1, ..., 4.4.x-dev, v5.0.0-BETA1, ..., 5.4.x-dev] but the package is fixed to v6.0.6 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- Root composer.json requires mika56/spfcheck * -> satisfiable by mika56/spfcheck[dev-master, 1.0.0, ..., 1.1.6, 9999999-dev].
Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.`
Hi
did I do something wrong ?
var_dump($checker->isIPAllowed('217.109.56.203', 'news2.cdiscount.com'));
[23-Aug-2016 00:00:29 Europe/Paris] PHP Fatal error: Uncaught exception 'Mika56\SPFCheck\Exception\DNSLookupException' in /apache_sites/jbm/spf/vendor/mika56/spfcheck/src/DNSRecordGetter.php:45
Stack trace:
#0 /apache_sites/jbm/spf/vendor/mika56/spfcheck/src/SPFCheck.php(143): Mika56\SPFCheck\DNSRecordGetter->resolveA('news2.cdiscount...')
#1 /apache_sites/jbm/spf/vendor/mika56/spfcheck/src/SPFCheck.php(85): Mika56\SPFCheck\SPFCheck->ipMatchesPart('217.109.56.203', 'a', 'news2.cdiscount...')
#2 /apache_sites/jbm/spf/vendor/mika56/spfcheck/src/SPFCheck.php(61): Mika56\SPFCheck\SPFCheck->doCheck('217.109.56.203', 'news2.cdiscount...')
#3 /apache_sites/jbm/spf/test.php(9): Mika56\SPFCheck\SPFCheck->isIPAllowed('217.109.56.203', 'news2.cdiscount...')
#4 {main}
thrown in /apache_sites/jbm/spf/vendor/mika56/spfcheck/src/DNSRecordGetter.php on line 45
When doing checks in bulk, having a cache on top of the DNS requests would be nice
Hi Mika,
I just did a check on a SPF record like this:
"v=spf1 a mx include:_spf.external.nl ~all"
The IP is allowed by the external spf and therefore I expected an allow, however the check returned Temp Error.
Turned out that the domain did not have MX records configured, and the doCheck routine catched this as an DNSLookupException and bailed out.
I don't know how this is in the specifications, but I would expect that such a thing would be ignored and the check continue with the next part.
Regards,
Michael
mika56/spfcheck 1.1.2 requires symfony/http-foundation 2.8.* || ^3.0 -> no matching package found
SPF Generated from code and updated in domain DNS. but showing pending in application (response return-pending) after propagation, and when i am trying to send email SPF getting passed, can you please help me fix this pending to verified issue
As of 4.6.1 of RFC 7208, mechanisms and modifiers are case-insensitive, but the lib is throwing an exception in case the mechanism is written as Ip4
, iP4
or IP4
(just as a few examples). The dns result should be converted to lowercase to handle such cases.
We're getting PHP Warning: dns_get_record(): An unexpected server failure occurred. in /path/to/vendor/mika56/spfcheck/src/DNS/DNSRecordGetter.php on line 18
when trying to validate SPF records for some domains. In our case the culprit seems to be in \Mika56\SPFCheck\DNS\DNSRecordGetter::resolveMx
method where an empty domain is returned and it then fails down the line.
dns_get_record('example.com', DNS_MX)
returns the following data
[
[
"host" => "example.com",
"class" => "IN",
"ttl" => 0,
"type" => "MX",
"pri" => 0,
"target" => "",
],
[
"host" => "example.com",
"class" => "IN",
"ttl" => 0,
"type" => "MX",
"pri" => 10,
"target" => "record.example.com",
],
]
And this means that resolveMx
method returns the following array ["", "record.example.com"]
which then fails in resolveA
method.
Probably need to add check in resolveMx
to skip entries with empty target
or automatically return false
in resolveA
if an empty domain is passed.
When i try to install through composer i get the following error:
`Using version ^1.0 for mika56/spfcheck
./composer.json has been updated
Loading composer repositories with package information Updating dependencies (including require-dev) Your requirements could not be resolved to an installable set of packages.
Problem 1
- Installation request for mika56/spfcheck ^1.0 -> satisfiable by mika56/spfcheck[1.0.0].
- Conclusion: remove symfony/http-foundation v3.1.8
- Conclusion: don't install symfony/http-foundation v3.1.8
- mika56/spfcheck 1.0.0 requires symfony/http-foundation 2.8.* -> satisfiable by symfony/http-foundation[v2.8.0, v2.8.1, v2.8.10, v2.8.11, v2.8.12, v2.8.13, v2.8.14, v2.8.15, v2.8.2, v2.8.3, v2.8.4, v2.8.5, v2.8.6, v2.8.7, v2.8.8, v2.8.9].
- Can only install one of: symfony/http-foundation[v2.8.13, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.14, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.15, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.0, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.1, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.10, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.11, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.12, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.2, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.3, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.4, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.5, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.6, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.7, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.8, v3.1.8].
- Can only install one of: symfony/http-foundation[v2.8.9, v3.1.8].
- Installation request for symfony/http-foundation (locked at v3.1.8) -> satisfiable by symfony/http-foundation[v3.1.8].
`
I guess its looking for an older version of http-foundation, maybe the requirements can be updated / changed for this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.