Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Pure Python SASL client and server library. The design of the library is intended to be agnostic of the protocol or network library.
The library currently offers PLAIN
and LOGIN
mechanisms by default. The
CRAM-MD5
, EXTERNAL
, and XOAUTH2
mechanisms are also available for special
circumstances.
There are currently no plans to implement security layer negotiation support.
Available in PyPi:
$ pip install pysasl
First, install Hatch. To run the build checks:
$ hatch run check
$ hatch run all:check # to run against all supported Python versions
Server-side SASL has three basic requirements:
Implementations may decide on any sub-set of mechanisms to advertise. Make this
choice when instantiating the SASLAuth
object:
from pysasl import SASLAuth
auth1 = SASLAuth.defaults() # equivalent to...
auth2 = SASLAuth.named([b'PLAIN', b'LOGIN'])
To get the names of all available mechanisms:
mechanisms = [mech.name for mech in auth1.server_mechanisms]
mech = auth1.get_server(b'PLAIN')
Once a mechanism has been chosen by the client, enter a loop of issuing challenges to the client:
def server_side_authentication(sock, mech):
challenges = []
while True:
try:
creds, _ = mech.server_attempt(challenges)
return creds
except ServerChallenge as chal:
sock.send(chal.data + b'\r\n')
resp = sock.recv(1024).rstrip(b'\r\n')
challenges.append(ChallengeResponse(chal.data, resp))
It's worth noting that implementations are not quite that simple. Most will
expect all transmissions to base64-encoded, often with a prefix before the
server challenges such as 334
or +
. See the appropriate RFC for your
protocol, such as RFC 4954 for SMTP or RFC 3501 for IMAP.
Once the challenge-response loop has been completed and we are left with the
a ServerCredentials
object, we can access information from the
attempt:
from pysasl.creds.server import ServerCredentials
from pysasl.identity import ClearIdentity, HashedIdentity
result: ServerCredentials = ...
print('Authenticating as:', result.authcid)
print('Authorizing as:', result.authzid)
# To compare to a known cleartext password...
identity = ClearIdentity('myuser', 's3kr3t')
assert result.verify(identity)
# Or to compare hashes...
from pysasl.hashing import BuiltinHash
identity = HashedIdentity('myuser, '$pbkdf2-sha256$500000$...', hash=BuiltinHash())
assert result.verify(identity)
# Or use passlib hashing...
from passlib.apps import custom_app_context
identity = HashedIdentity('myuser', '$6$rounds=656000$...', hash=custom_app_context)
assert result.verify(identity)
The goal of client-side authentication is to respond to server challenges until the authentication attempt either succeeds or fails.
The first step is to pick a SASL mechanism. The protocol should allow the server to advertise to the client which mechanisms are available to it:
from pysasl import SASLAuth
auth = SASLAuth.named(advertised_mechanism_names)
mech = auth.client_mechanisms[0]
Any mechanism name that is not recognized will be ignored.
Once a mechanism is chosen, we enter of a loop of responding to server challenges:
from pysasl.creds.client import ClientCredentials
def client_side_authentication(sock, mech, username, password):
creds = ClientCredentials(username, password)
challenges = []
while True:
resp = mech.client_attempt(creds, challenges)
sock.send(resp + b'\r\n')
data = sock.recv(1024).rstrip(b'\r\n')
if data == 'SUCCESS':
return True
elif data == 'FAILURE':
return False
challenges.append(ServerChallenge(data))
As you might expect, a real protocol probably won't return SUCCESS
or
FAILURE
, that will depend entirely on the details of the protocol.
Some protocols (e.g. SMTP) support the client ability to send an initial response before the first server challenge, for mechanisms that support it. A perfectly valid authentication can then have no challenges at all:
AUTH PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk
235 2.7.0 Authentication successful
In this case, both client-side and server-side authentication should be handled a bit differently. For example for server-side:
challenges = []
if initial_response:
challenges.append(ChallengeResponse(b'', initial_response))
And for client-side, just call resp = mech.client_attempt(creds, [])
to get the initial response before starting the transmission. All
mechanisms should either return an initial response or an empty string
when given an empty list for the second argument.
FAQs
Pure Python SASL client and server library.
We found that pysasl demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.