I'm trying to figure out if it is possible to have something like this scenario:
Say we have two people, Alice and Bob. Alice wants to send some data (doesn't matter what this data is) to Bob, and vice versa. I know that WebRTC can be used to serverless-ly exchange messages, but that requires Alice and Bob knowing each other's IP addresses. Now, it's relatively easy for Alice and Bob to share their IP addresses once, to initialize a connection, but what happens if one of them happens to connect to a different network; maybe Bob is in a coffee shop, for instance, and his IP address is thus different? The previously initialized connection wouldn't be to his current IP address, so they'd have to reinitialize the connection; but how?
It would seem to me that there would already need to be some sort of preexisting communication between the two so they could share their IP addresses, but then why not just communicate through the method they communicate their IP addresses instead? Alternatively, there could be a server that connects the two, but that defeats the serverless part of the system.
So, is there any way to maintain communication between two clients even if they happen to change networks, and thus IP addresses? Perhaps there is a more fixed method of identifying devices than their IP addresses, like I've seen in this SO answer, but it's years old, so maybe there's something new? I'd be implementing this in JS, across multiple devices/OSs, so that answer probably wouldn't work. Any ideas/examples would be greatly appreciated; I mainly want to know if this is even possible, and, if so, how.
Simple Answer
No, while this is possible, it is unrealistic and not needed. See my longer explanation below. Also, if you really want to do this, although it is possible through QUIC, it’s not likely to be needed (as explained below).
Longer Answer/Question to think about
In short, this is not a needed feature of WebRTC. Let me ask you a question:
Alice and Bob are in a data channel, exchanging chat messages over WebRTC. To create a WebRTC connection, you need to use an ice server ( first link, second link ) to get both Alice and Bob to, to quote Wikipedia:
...to find ways for the two computers to talk to each other as directly as possible..
This means that it will use Alice’s current IP address to make an offer to Bob through a STUN or TURN server. If, like you said, Alice were to change IP addresses, she would need to change location. That means that she will need to move a sufficient distance so that the IP address will change. In practice, this probably means that she goes in a car and drives somewhere. If not, she calls an Uber or a cab or rides her bike. In most of these scenarios, she will need to close her computer, hence ending the p2p connection. If, by some weird wizardry, she doesn’t close her computer/the connection, the browser will very likely refresh, hence re-connecting to the WebRTC data channel from the new IP address. When will you need to create a WebRTC data channel and handle IP changes? Long explanation coming to a conclusion, clients changing IP addresses without ending/resetting the connect just doesn’t happen in practice.
If you want to look into other alternatives, here are some examples:
ALTERNATIVES
Adding an IP event listener ★
Now this isn’t an actual global variable that you can check, but you can use an online API (some are listed here) to check the user’s IP address, store it in a variable (or localStorage), and check if the IP changes. In a loop, you would do some simple logic to check If it does, you reset the WebRTC connection, if not, you keep the loop going.
Using a “piping” server
You can set up a simple chat by using a http/https server, already set up, explained here, called a piping server. You can look at the article for more information, but it promotes a serverless chat system (can be used without creating a server) that isn’t exposed to the difficulties of changing IP addresses. However, you need to know the peer’s ID, and they need to know your ID, which effectively makes solution obsolete because you need to have some sort of communication before establishing this simple chat.
Using Node.js, Websockets, and/or Socket.io ★
If you want to create a simple chat app or create a data channel, Node.js and Socket.io is the way to go. This is super simple, however, it involves a server, which is why I left it for last. However, I highly recommend this for ease and simplicity, and is not reliant on IP addresses. See here for a very good starting immersion into Node.js and the Express framework. I am far from an expert from Websockets, but MDN is always a good place to start. However good Websockets are, I think that Socket.io is much easier for beginners, so if you are willing to sacrifice a bit of speed over simplicity, you should start here. These are all good server-side chat starting points.
Links
Simple Answer
QUIC connection migration
IP Listener
SO Question,
Ice Servers
MDN docs, Wikipedia
Piping Server
Simple article, Github repo
Node.js, Websockets, and Socket.io
Node.js and Express setup, Websocket intro, and Socket.io intro
All alternatives that are starred (★) are personally recommended.
Yes, It is possible.
You need to use FQDN (or a sub-domain) instead of IP Address and a DNS server, and a client side util or tool to update DNS record while IP Address changing.
There's free solutions on the web like no-ip.com, cloud-flare and etc.
A more modern approach to this would be using QUIC for transport. It has a session ID in the payload and uses UDP as the transport. This handles the very common case where a NAT will change it's Public IP. From Cloudflare's blogpost:
One of the features QUIC is intended to deliver is called “connection migration” and will allow QUIC end-points to migrate connections to different IP addresses and network paths at will.
So assume Alice and Bob are sending messages via QUIC and their connection is given a session ID. Alice's NAT changes it's public IP and source port. Since UDP is being used, Alice's messages still are being sent to Bob's Public IP. Bob receive these UDP messages and looks at the embedded QUIC header and sees that it contains the same session ID as when he was speaking to Alice. Bob then starts using Alice's new public IP and port as a destination for the conversation.
Naturally this seems open to redirection attacks, but there is crypto layered on top of these mechanisms to prevent this and other attacks.
References:
Cloudflare Blog
Amazing blog on NAT behavior and setting up initial connectivity behind them
QUIC connection migration
Related
I am very new to websockets and trying to create an omegle as an example. A centralized chat server to which every client can connect through a websocket and the the server matches people based on interests. The people can chat through the websocket connection thereafter.
I am just curious about one thing: Once my site goes live and different clients keep connecting, essentially in the background they are connecting to my central server through the websocket. Can't any client run javascript on its chrome console and inject a malicious script or get access to the clients connected to the server already since the connection has been established and its a stateful connection? I am not sure if there is a way to do that. And if there is, what security mechanisms i need to take care of?
The response to your question is "Yes" and "No"!
it depends on so many things. First, is your chat a public one? I mean can every body join the chat room without login?
In this case i guess everybody can chat so a guy who want to use your websockets can do so a it is public.
In the case your chat is private it is your own to protect your websockets events from being accessed by everybody. This start by registering users after a robust login process.
As all others Internet (and computer based) tech, websocket is subject to hacking. You chould protect your websockets as you protect your web based developments.
Typing "websocket common attacks" in Google will return you links that talk of websocket security.
Yes, a client can definitely inject a malicious script... but it will only affect the client's own browser.
No. There isn't a way to do that unless you explicitly created a vulnerability.
You see, first of all, websockets are not peer-to-peer connections. They require a server to transmit messages between end users, so if the server doesn't do anything about an incoming message request, the user the first user intended to send a message to would not receive anything. Essentially, this means that websocket clients can only connect to users if the server lets them.
However, even if websockets were p2p connections that would still not be possible, or what kind of a web would we have?! I hope you understand that any data sent to a client isn't automatically executed as code. It usually is in a string format and it will never be able to run any code unless you actually eval it or something. (Or you forget about XSS and don't properly escape data that would be rendered as HTML.)
So, to answer your question, no and no. Security measures? Don't eval incoming data.
However, what you do have to be aware of in websockets is that any client can connect (well, at least try) to a websocket server. See, because websockets usually are used to send data between clients, when a server receives a message, it usually will broadcast that message to everyone in that connection/room except for the sender. This means that a user with malicious intent may enter your room and send data, as well as receive data that's probably meant to be private.
To prevent this, ensure you are setting up proper authentication. You can do this through cookies, perhaps, used with session or JWT (I usually use the latter). When a user joins a room or connects, check if that user is authorized and allowed to be in the room. Otherwise, reject the connection.
Here's a bit broader explanation that might help you.
Web sockets work on TCP connections. That means the socket connects to a specific destination and transmits data ("messages"). Those messages can flow both ways (in and out). Users connect to your server using clients (most commonly a browser).
The connections are unique, meaning no two clients share the same connection. Therefore, no one else knows what others send to or receive from the server.
Messages can be of various types, but the most commonly used is a string. Typically people encode some JSON objects in those strings to perform specific actions. Something along the lines of:
{
message: "Hello!",
channel: "general"
}
Now here comes your role. How to act on those messages, and what to send to whom. Standard libraries provide functionalities like targeting a single socket or sending messages to many sockets. Imagine it like a list of connections. They can send specific commands to join/leave channels, so you add more meta data to each. In the previous example, you might want to send Hello! to every connection that has "joined" a channel (by sending a specific command to do so).
You decide what the commands are:
{
command: "join/leave",
channel: "general"
}
You might want to spread every message you receive to others or keep it to yourself (and execute some custom functionality). Nothing happens out of the box - the server receives the message, and that's it.
All of the above means that whatever users do with their clients won't affect you or others. The only way you can know of their actions is if they send you a message. Those messages you must be careful with, as they can contain any malicious code.
Therefore I'd highly recommend you use some library that deals with web sockets in case you'd like to ship something to people. If it's for learning purposes - stick to the plain functionalities.
Say I want to limitate the amount of connections on a single device to my WebSocket server. For this I can compare the IP addresses, and reject duplicate IP’s (if maximum connections are 1 per device). But if two devices try to connect to my server using the same network IP, the last one will be rejected.
Is there another way to identify one device and reject if exceed max amount of WebSocket connection?
I am using Node.js with websocket/ws module.
The only real solution here is to require an account login before getting any meaningful access to your service and to rigorously control how accounts are created and handed out. Then, you can easily prevent multiple access from the same account by just checking if the account is already and currently logged in. This approach also allows you to implement rate limiting, service abuse detection and even account banning (either temporal or permanent) when repeated abuse is detected.
IP address is not a meaningful solution because you don't have any way of knowing what the real IP address is of the client when they are behind a proxy or NAT and trying to use the IP address in that case can end up falsely blocking lots of legitimate users because they may all share one common internet IP address even though they all have their own private IP address on their own local network. Contrary to what Myst wrote, your load balancer layer can't reliably get access to the real client IP address either so this isn't an issue of application layer vs. network layer. It's an issue where your end of the connection (at all layers) does not necessarily have access to the real client IP address (because of client-side proxies and NAT).
If you just want to erect some obstacles to prevent casual or accidental login from the same browser and there's some reason you don't want to require account login, then you can cookie the browser upon first access and then on subsequent accesses check to see if a browser with that cookie is already connected. This is not the least bit secure. It's trivial to defeat (just use a second browser, second computer or disable cookies), but it does prevent users from accidentially doing this and may even keep some non-sophisticated users from doing it on purpose. But, it is easy to defeat so the cookie protection is, at best, a weak obstacle.
I'm aware that this might not be the answer you want, but it the answer you will probably get when all is said and done:
Connection limiting (which is in essences a security related concern) cannot be safely resolved by the application layer. This should be resolved by the proxy (or, sometimes, the load balancer) layer, and even this approach isn't fully reliable (due to limited data and the multitude of intermediaries involved in internet connections).
Consider that the remote IP your application has access to isn't the real IP address.
When collecting the data from the socket layer, the application will either have access to the proxy's IP address, the host layer or some other intermediary... but not the client's actual IP address.
When collecting data from the HTTP layer (as performed by remoteAddress) the data collected isn't reliable. It's provided by the client and can be easily tampered with, forged, spoofed, etc'.
Simply put, the application doesn't have enough data to actually implement this security measure.
In other words, this layer of security should be implemented before your application is exposed to the incoming connection, not by the application layer itself.
The solution suggested by #NewToJS is by far the best approach an application can master - limiting access by using a login system, where only registered users can establish a persistent connection and each user has a limit on the number of possible connections (often disconnecting the oldest connection is better than refusing the new connection).
I'm using websockets for the first time and am unsure what I am doing wrong. Due to my IP being dynamic, I am using the following websocket call serverside:
$echo = new echoServer("myurl.com","9000");
and the following call clientside:
socket = new WebSocket("wss://myurl.com:9000");
I get a timeout err even though the server is completely unoccupied. Does anyone know why?I feel this is a basic error
That specific server, PHP-Websockets, does not provide support for the wss:// protocol (secure connection, similar to https:// for HTTP traffic).
Change the connection line to:
socket = new WebSocket("ws://myurl.com:9000");
Alternatively, if you need TLS support, PHP-Websockets might not be the best server for you.
I made the decision to not support TLS in PHP-Websockets because I did not know of any robustly tested, battle hardened PHP packages that provide access to the OpenSSL libraries. I have changed my mind as of 5 minutes ago, and will implement TLS using the built in PHP OpenSSL functions, but this will take time to implement and will require several testers. If you don't want to wait, don't wait.
I do not want people to think that their data is secure when I simply can't guarantee it.
Also, not specifically causing a failure but still an issue: In your server-side code, you should change instantiating the server's object to:
$echo = new echoServer("0.0.0.0", "9000");
The reason is that the IP address is setting the server's listen address. The listen address of "0.0.0.0" gets translated to "listen on all IP addresses that this machine can listen to." That means that, besides just your own public IP, it will also listen to 127.0.0.1, and if you have a second network card that has a different IP address assigned to it, you can receive traffic from that as well.
However, because of PHP's weak typing, when socket_bind() attempts to convert the IP address into its binary representation, but finds a string instead, it silently converts it to 0, which is equivalent to the IP address 0.0.0.0.
(I previously thought that socket_bind() was performing a DNS lookup to get the IP address. I was very wrong. Such weak typing on the part of PHP, and especially the assumptions made by developers like me, is the cause of many security holes.)
I've written a Go server with custom binary websocket protocol, and a Dart client. User authentication on the server employs scrypt and the recommended parameters N=16384, r=8, p=1 (with salt of length 16 and generated key length of 64), my i7 desktop takes maybe a second or two to crank through authentication on the server side. That's compared to practically instant, say, SHA-512 authentication.
I had trouble finding scrypt implementations for Dart and while this one works, generating the same hash with the same parameters in a browser (Firefox) takes too long to practically complete. I can get it down to a handful of seconds on the same machine using N=1024 and r<=8 but if I settle on that for compatibility, on the server side, the authentication time is for practical purposes instant again.
Scrypt is great on the server side but I'm wondering if its practical for a browser client. Admittedly I haven't seen any/many examples of people using scrypt for browser authentication. Should I persevere and tackle the performance (e.g. maybe using other javascript libraries from dart), or is this a basic limitation at the moment? How low can you wind down the scrypt parameters before you may as well just use more widely available, optimised crypto hashing algos such as SHA?
Use HTTPS. If you're hashing the password in the browser and then sending the hash to the server for comparison, what's to prevent an attacker from simply sniffing the hashed password and hijacking the session by sending the same hash himself?
Even if you come up with an encryption scheme to prevent that, the attacker could simply inject an additional <script> tag with a keylogger via a MITM attack to steal the password before it's encrypted.
Basically no matter how you cut it, you have to use HTTPS to ensure that your communications are not sniffable and no MITM attack has taken place. And once your connection is already secured over HTTPS, which is encrypted with a (minimum) 128-bit key and would take longer than the known age of the universe to crack, you might as well just use the HTTPS connection to send your password and doing additional encryption of the password client-side is probably not necessary.
#maaartinus ...
I've never thought about not using HTTPS. I'm curious if offloading the password-based key derivation overhead to the client makes any sense.
If I may, I can come at this problem from non-Web direction and come back to the browser-use case. Way back when, I worked with EFT*POS security standards and working with secure communications for financial transactions. For example; the credit-card machine in the supermarket. Just establish my grounding on this topic. That said, I think the original question HAS be covered comprehensively. I decided to add a comment to enrich the conversation on this area (it is quite topical).
The procedure is about the a conversation between the terminal (iPhone, smartphone, browser, etc). Premise: you naturally don't want anyone sniffing your username/password pairing. Assume your typical web page or login screen at work. Over the Intranet, LAN, WAN and VPN what every you type is dispatched from your keyboard to the host. These links may already be encrypted these days. The WWW web on the Internet has two main options: HTTP (clear text) and HTTPS (encrypted) via the browser. If we just stick with the (username, password) pair.
Your terminal (such as browser or mobile) needs to be "trusted" by the host (server, phone company, etc).
There's a lot of standard stuff you can (should do) first; and get creative from there-on. Think of it as a pyramid. On the bottom are things you can do on your PC. That's the base of the pyramid. And there's loads of good information about that (e.g. Electronic Frontier Foundation, EFF), it is about protecting yourself, your data (intangible property) and your rights.
That said here are a few points to consider:
Everything sent via HTTP is clear-text. It can be read and copied.
A hash sent over clear-text can be decoded to get the password. It is just maths.
Even if you use scrypt or another method the hash is decodable -- Given enough time.
If you're on the web, any hash implemented in the browser (terminal/client), is transparent to anyone who can load the web page and javascript code [pointed out by ntoskrnl above.
HTTPS on the other hand, sends Everything-as-a-hash. In addition the hash used is negotiated is unique to the conversation and agreed at session-completion time. It is a slightly 'better-er' hash over the whole-of-the-Message.
The main thing making it better-er in the first instance, is the negotiation. The idea overall is that message hashes are based on a key only known to both end-points.
Once again that can be cracked if you have enough time, etc. The main thing making this challenging is establishing the to-and-fro for the negotiations.
Let's back-up a little and consider cryptography. The notion is to hide the message in a way that permits the message to be revealed. Think of this a as lock and key, where the door is the procedure/algorithm and your message is the contents of the room.
HTTPS works to separate the lock from the key in a pragmatic fashion, in time and via process.
Whatever is done in the HTTPS room, stays in the HTTPS-room. You must have the key to enter, poke about and do unwanted stuff. Imho, any extra security should be ONLY considered within a HTTPS space.
There are methods to improve on that foundation. I think of security like a pyramid. about 4 or 5 layers above the base considerations like the transport protocol.
Such options include, ...
SMS authentication number to your phone.
Something like a dongle or personalised ID-key.
An physical message like an s-mail or e-mail with an authentication Number.
anything you come up with.
In summary, if your need say make it safe; there are many things that can be accomplished. If you can't use HTTPS, hashing password(s) locally needs to be managed extremely carefully. Hashes have vulnerabilities. When you are not using HTTPS, anything you can do in the browser is like wet-rice-paper trying to stave-off a sword.
I am using the basic-auth twitter API (no longer available) to integrate twitter with my blog's commenting system. The problem with this and many other web APIs out there is that they require the user's username and password to do anything useful. I don't want to deal with the hassle and cost of installing a SSL certificate, but I also don't want passwords passed over the wire in clear text.
I guess my general question is: How can I send sensitive data over an insecure channel?
This is my current solution and I'd like to know if there are any holes in it:
Generate a random key on the server (I'm using php).
Save the key in a session and also output the key in a javascript variable.
On form submit, use Triple DES in javascript with the key to encrypt the password.
On the server, decrypt the password using the key from the session and then destroy the session.
The end result is that only the encrypted password is sent over the wire and the key is only used once and never sent with the password. Problem solved?
Generate a random key on the server (I'm using php).
Save the key in a session and also output the key in a javascript variable.
On form submit, use Triple DES in javascript with the key to encrypt the password.
This avoids sending the password in the clear over the wire, but it requires you to send the key in the clear over the wire, which would allow anyone eavesdropping to decode the password.
It's been said before and I'll say it again: don't try to make up your own cryptographic protocols! There are established protocols out there for this kind of thing that have been created, peer reviewed, beat on, hacked on, poked and prodded by professionals, use them! No one person is going to be able to come up with something better than the entire cryptographic and security community working together.
Your method has a flaw - if someone were to intercept the transmission of the key to the user and the user's encrypted reply they could decrypt the reply and obtain the username/password of the user.
However, there is a way to securely send information over an unsecure medium so long as the information is not capable of being modified in transit known as the Diffie-Hellman algorithm. Basically two parties are able to compute the shared key used to encrypt the data based on their conversations - yet an observer does not have enough information to deduce the key.
Setting up the conversation between the client and the server can be tricky though, and much more time consuming than simply applying SSL to your site. You don't even have to pay for it - you can generate a self-signed certificate that provides the necessary encryption. This won't protect against man-in-the-middle attacks, but neither will the Diffie-Hellman algorithm.
You don't have to have a certificate on your server; it's up to the client whether they are willing to talk to an unauthenticated server. Key agreement can still be performed to establish a private channel. It wouldn't be safe to send private credentials to an unauthenticated server though, which is why you don't see SSL used this way in practice.
To answer your general question: you just send it. I think your real general question is: “How do I send sensitive data over an insecure channel—and keep it secure?” You can't.
It sounds like you've decided that security isn't worth the $10–20 per month a certificate would cost, and to protect Twitter passwords, that's probably true. So, why spend time to provide the illusion of security? Just make it clear to your users that their password will be sent in the clear and let them make their own choice.
So how is this any more secure? Even though you might have secured browser<>your server, what about the rest of the Internet (your server<>twitter)?
IMHO, it's unacceptable to ask for a username and password of another service and expect people to enter that. And if you care that much - don't integrate them until they get their act straight and re-enable OAuth. (They supported it for a while, but disabled it a few months ago.)
In the mean time, why not offer OpenID? Every Google, Yahoo!, VOX etc. account has one. People might not be aware of it but chances are really, really high that they already have OpenID. Check this list to see what I mean.
When the key is sent between the client and the server it is clear text and subject to interception. Combine that with the encrypted text of the password and the password is decrypted.
Diffie-Hellman is a good solution. If you only need to authenticate them, and not actually transmit the password (because the password is already stored on the server) then you can use HTTP Digest Authentication, or some variation there of.
APIs and OAuth
Firstly, as others have said, you shouldn't be using a user's password to access the API, you should be getting an OAuth token. This will allow you to act on that user's behalf without needing their password. This is a common approach used by many APIs.
Key Exchange
If you need to solve the more general problem of exchanging information over insecure connections, there are several key exchange protocols as mentioned by other answers.
In general key exchange algorithms are secure from eavesdroppers, but because they do not authenticate the identity of the users, they are vulnerable to man-in-the-middle attacks.
From the Wikipedia page on Diffie Hellman:
In the original description, the
Diffie–Hellman exchange by itself does not provide authentication of
the communicating parties and is thus vulnerable to a
man-in-the-middle attack. A person in the middle may establish two
distinct Diffie–Hellman key exchanges, one with Alice and the other
with Bob, effectively masquerading as Alice to Bob, and vice versa,
allowing the attacker to decrypt (and read or store) then re-encrypt
the messages passed between them. A method to authenticate the
communicating parties to each other is generally needed to prevent
this type of attack. Variants of Diffie-Hellman, such as STS, may be
used instead to avoid these types of attacks.
Even STS is insecure in some cases where an attacker is able to insert their own identity (signing key) in place of either the sender or receiver.
Identity and Authentication
This is exactly the problem SSL is designed to solve, by establishing a hierarchy of 'trusted' signing authorities which have in theory verified who owns a domain name, etc, someone connecting to a website can verify that they are indeed communicating with that domain's server, and not with a man-in-the-middle who has placed themselves in between.
You can create a self-signed certificate which will provide the necessary configuration to encrypt the connection, but will not protect you from man in the middle attacks for the same reason that unauthenticated Diffie-Hellman key exchange will not.
You can get free SSL certificates valid for 1 year from https://www.startssl.com/ - I use them for my personal sites. They're not quite as 'trusted' whatever that means, since they only do automatic checks on people who apply for one, but it's free. There are also services which cost very little (£10/year from 123-Reg in the UK).
I've implemented a different approach
Server: user name and password-hash stored in the database
Server: send a challenge with the form to request the password, store it in the session with a timestamp and the client's IP address
Client: hash the password, concat challenge|username|passwordhash, hash it again and post it to the server
Server: verify timestamp, IP, do the same concatenation/hashing and compare it
This applies to a password transmission. Using it for data means using the final hash as the encryption key for the plain text and generating a random initialization vector transmitted with the cipher text to the server.
Any comments on this?
The problem with client-side javascript security is that the attacker can modify the javascript in transit to a simple {return input;} thereby rendering your elaborate security moot. Solution: use browser-provided (ie. not transmitted) RSA. From what I know, not available yet.
How can I send sensitive data over an
insecure channel
With a pre-shared secret key. This is what you attempt in your suggested solution, but you can't send that key over the insecure channel. Someone mentioned DH, which will help you negotiate a key. But the other part of what SSL does is provide authentication, to prevent man-in-the-middle attacks so that the client knows they are negotiating a key with the person they intend to communicate with.
Chris Upchurch's advice is really the only good answer there is for 99.99% of engineers - don't do it. Let someone else do it and use their solution (like the guys who wrote the SSL client/server).
I think the ideal solution here would be to get Twitter to support OpenID and then use that.
An ssl certificate that is self-signed doesn't cost money. For a free twitter service, that is probably just fine for users.
TO OLI
In your approch for example i'm in the same subnet with same router, so i get the same ip as my collegues in my work. I open same url in browser, so server generates the timestamp with same ip, then i use tcp/ip dump to sniff the hashed or non hashed password from my collegues connection. I can sniff everything he sends. So i have all hashes from his form also you have timestamp(my) and same ip. So i send everything using post tool and hey i'm loggen in.
If you don't want to use SSL, why not try some other protocol, such as kerberos?
A basic overview is here:
http://www.kerberos.org/software/tutorial.html
Or if you want to go somewhat more in depth, see
http://www.hitmill.com/computers/kerberos.html
I have a similar issue(wanting to encrypt data in forms without paying for an ssl certificate) so I did some hunting and found this project: http://www.jcryption.org/
I haven't used it yet but it looks easy to implement and thought I'd share it here in-case anyone else is looking for something like it and finds themselves on this page like I did.