I think there's a fairly serious gap in the recently-introduced cc number encryption, given that the server will be storing data (almost always on disk) for all active checkout sessions. This encrypted data can persist for uncompleted orders for as long as PHP retains its session data, which can be days or weeks. However, both the encrypted data and encryption key are stored with the same open access levels (these files are almost always world-readable -- PHP creates them that way -- at least on Apache).
Although offering a slight level of security over session ID leakage, that form of attack was never the real issue when storing cc numbers in sessions. An attacker with any level of filesystem access (e.g., by exploiting another Drupal module or poor permissions on shared access servers) to gain access to both the session data and the encryption key, giving access to the cc numbers & billing details of all the uncompleted orders sitting on the server. This changes the position of Ubercart for PCI DSS compliance; and given that it's very easy to sniff for Ubercart sites (the footer messages give it away), it's not infeasible that Ubercart store owners could be targeted.
In the short term, to mitigate somewhat against these kind of attack, the uc_credit.key file should certainly be chmodded to 0600 when created, although this would only protect against some shared-server attacks and not against vulnerabilities in Drupal and other modules.
A PCI DSS compliant level of protection could be offered by not storing the cc number in a session, but simply propagating it via POST on cart/checkout and cart/checkout/review; and (if the user selects this option) when storing the cc number in the database after checkout, by encrypting it against a user-entered admin password (which would *not* be stored permanently on the server). The following proposal also has the advantage of protecting against a wide variety of attacks except alterations to the executing code or a specific admin-user directed client-side attack.
My idea for how this would work is detailed below:
*Setting up the encryption*
The admin user enters his special password, at which point all current db entries are re-encrypted with this key combined with the file-based encryption key to encrypt the cc data in the db. The password is not stored in a session, and thus never leaves POST. The database can be re-encrypted whenever the password expires -- and there should at least be an option for enforced expiry.
*Retrieving cc numbers from the db in admin/store/orders*
1) Admin user enters password when prompted.
2) Half of the admin password is stored in a time-limited db session (cleared using a small cleanup routine in hook_cron), the other half is stored in a client-side cookie and thus is never accessible anywhere on disk (unless the user is logging POST data, in which case cc numbers would already be available in plaintext). This allows the admin to browse through several orders with cc details without having to repeatedly enter the password -- this would only cause the user to be less careful about guarding their password.
3) Whenever the card number is retrieved from the database, both halves of the special admin password along with the key in uc_credit.key are combined to decrypt the number.
4) A log is retained of the Drupal user who has viewed the card number in order to satisfy the PCI DSS.
(A slightly better form of (2) is possible if the second half of the password were stored in a cookie using JS on the client side and never sent to the server. None of this would be able to keep out a good XSS or keylogger attack, but that is beyond what PCI DSS deals with.)
Obviously, HTTPS needs to be used on admin/store/orders (at the very least) in order for this to work. HTTPS should also be configured for cart/checkout, and in my view cc numbers should only be enterable or retrievable via https, unless in some kind of debug mode because otherwise a non tech-savvy store owner could leave themselves wide open without realising (and in my experience, they will always do this if given the opportunity, therefore default permissions should always be restrictive).